-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathindex.html
845 lines (799 loc) · 50.6 KB
/
index.html
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
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
<!DOCTYPE html>
<html>
<title>NoraUi</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<link rel="stylesheet" href="https://www.w3schools.com/lib/w3-theme-teal.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Raleway">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="css/prism.css" />
<link rel="icon" href="favicon.ico">
<style>
html,body,h1,h2,h3,h4,h5 {font-family: "Raleway", sans-serif}
</style>
<script type="text/javascript" src="js/prism.js"></script>
<body class="w3-light-grey">
<!-- Top container -->
<div class="w3-bar w3-top w3-large w3-card" style="z-index:4; background-color:rgba(52, 73, 94, 0.7)!important;color:#fff;">
<button class="w3-bar-item w3-button w3-hide-large w3-hover-none w3-hover-text-light-grey" onclick="w3_open();"><i class="fa fa-bars"></i> Menu</button>
<span class="w3-bar-item w3-right"><a href="/~https://github.com/NoraUi/noraui.github.io/edit/master/index.html" target="_blank" style="text-decoration:none; font-size:15px;"><i class="fa fa-edit"></i> Edit this page</a></span>
<span class="w3-bar-item w3-left">Non-Regression Automation for User Interfaces</span>
</div>
<!-- Sidebar/menu -->
<nav class="w3-sidebar w3-collapse w3-white w3-animate-left" style="z-index:3;width:300px;" id="mySidebar">
<div class="w3-container w3-row" style="margin-top: 10px;">
<div class="w3-col s4">
<a href="#"><img style="width: 90%" src="img/noraui.png" alt="NoraUi"/></a>
</div>
<div class="w3-col s8 w3-bar">
<span style="padding-left: 15px;">NoraUi</span><br>
<a href="/~https://github.com/NoraUi" target="blank" class="w3-bar-item w3-button"><i class="fa fa-github"></i></a>
<a href="/~https://github.com/orgs/NoraUi/people" target="blank" class="w3-bar-item w3-button"><i class="fa fa-users"></i></a>
<a href="/~https://github.com/NoraUi/NoraUi" target="blank" class="w3-bar-item w3-button"><i class="fa fa-code"></i></a>
<a href="https://stackoverflow.com/search?q=noraui" target="blank" class="w3-bar-item w3-button"><i class="fa fa-stack-overflow"></i></a>
</div>
</div>
<hr>
<div class="w3-container">
<h5>Developer guide</h5>
</div>
<div class="w3-bar-block">
<a href="#" class="w3-bar-item w3-button w3-padding-16 w3-hide-large w3-dark-grey w3-hover-black" onclick="w3_close()" title="close menu"><i class="fa fa-remove fa-fw"></i> Close Menu</a>
<a href="#" class="w3-bar-item w3-button w3-padding menu-item" onclick="goTo(this);"><i class="fa fa-eye fa-fw"></i> Overview</a>
<a href="#concepts" class="w3-bar-item w3-button w3-padding menu-item" onclick="goTo(this);"><i class="fa fa-lightbulb-o fa-fw"></i> Concepts</a>
<a href="#installationInstructionsEclipse" class="w3-bar-item w3-button w3-padding menu-item" onclick="goTo(this);"><i class="fa fa-book fa-fw"></i> Installation Instructions from Eclipse</a>
<a href="#installationInstructionsCmd" class="w3-bar-item w3-button w3-padding menu-item" onclick="goTo(this);"><i class="fa fa-book fa-fw"></i> Installation Instructions from Cmd</a>
<a href="#quickStartGuide" class="w3-bar-item w3-button w3-padding menu-item" onclick="goTo(this);"><i class="fa fa-graduation-cap fa-fw"></i> Quick Start Guide</a>
<a href="#commandLineInterface" class="w3-bar-item w3-button w3-padding menu-item" onclick="goTo(this);"><i class="fa fa-terminal fa-fw"></i> CLI</a>
<a href="#javadoc" class="w3-bar-item w3-button w3-padding menu-item" onclick="goTo(this);"><i class="fa fa-book fa-fw"></i> Official java doc</a>
<a href="#continuousIntegration" class="w3-bar-item w3-button w3-padding menu-item" onclick="goTo(this);"><i class="fa fa-cogs fa-fw"></i> Continuous Integration</a>
<a href="#zap" class="w3-bar-item w3-button w3-padding menu-item" onclick="goTo(this);"><i class="fa fa-bolt fa-fw"></i> OWASP Zed Attack Proxy</a>
<a href="#customDataProvider" class="w3-bar-item w3-button w3-padding menu-item" onclick="goTo(this);"><i class="fa fa-database fa-fw"></i> Custom data provider</a>
<a href="#ourClients" class="w3-bar-item w3-button w3-padding menu-item" onclick="goTo(this);"><i class="fa fa-thumbs-o-up fa-fw"></i> Our clients</a><br><br>
</div>
</nav>
<!-- Overlay effect when opening sidebar on small screens -->
<div class="w3-overlay w3-hide-large w3-animate-opacity" onclick="w3_close()" style="cursor:pointer" title="close side menu" id="myOverlay"></div>
<!-- !PAGE CONTENT! -->
<div class="w3-main" style="margin-left:300px;margin-top:43px;">
<!-- Header -->
<header class="w3-container">
<h2><i class="fa fa-eye"></i> Overview</h2>
</header>
<div class="w3-row-padding">
<p>NoraUi, for NOn-Regression Automation for User Interfaces, is a <b>Java framework</b> based on <b>Selenium, Cucumber and Gherkin stack</b> to create <b>GUI testing projects</b> that can be included in the continuous integration chain of single/multi applications web solution builds.<br/>
It ensures applications non-regression throughout their life taking into account code evolutions and acceptance of defined business client criteria.
</p>
</div>
<div class="w3-row-padding w3-center">
<div class="w3-third">
<div class="w3-card-2" style="min-height: 330px" >
<div class="w3-container w3-green"><h3>Manage your data</h3></div>
<br /> <i class="fa fa-database w3-margin-bottom w3-text-green" style="font-size: 70px"></i>
<p>Choose your Input Data Provider<br/>
Excel (xls, xlsx, xlsm), CSV<br/>
REST Web Services<br/>
SQL (MySql, Postgres, Oracle)<br/>
...<br/>
or implement yours<br/>
</p>
</div>
</div>
<div class="w3-third">
<div class="w3-card-2" style="min-height: 330px">
<div class="w3-container w3-blue"><h3>Added features</h3></div>
<br /> <i class="fa fa-cogs w3-margin-bottom w3-text-blue" style="font-size: 70px"></i>
<p>Reworked Selenium methods<br/>
Error management<br/>
Reporting<br/>
Conditioned steps<br/>
Loop steps<br/>
Configuration management<br/>
</p>
</div>
</div>
<div class="w3-third">
<div class="w3-card-2" style="min-height: 330px">
<div class="w3-container w3-red"><h3>Run from anywhere on everything</h3></div>
<br /> <i class="fa fa-desktop w3-margin-bottom w3-text-red" style="font-size: 70px"></i>
<p>Windows/Unix/Mac OS portability<br/>
Independent of front-end web apps technologies<br/>
</p>
</div>
</div>
</div>
<div class="w3-row-padding">
<p>Many, perhaps most, software applications today are written as web-based applications to be run in an Internet browser. The effectiveness of testing these applications varies widely among companies and organizations. In an era of highly interactive and responsive software processes where many organizations are using some form of Agile methodology, test automation is frequently becoming a requirement for software projects. Test automation is often the answer. Test automation means using a software tool to run repeatable tests against the application to be tested. For regression testing this provides that responsiveness.</p>
<p>There are many advantages to test automation. Most are related to the repeatability of the tests and the speed at which the tests can be executed. There are a number of commercial and open source tools available for assisting with the development of test automation. Nora-UI is probably the open source solution you can use. This user's guide will assist new users in learning effective techniques in building test automation for web applications.</p>
<p>This user's guide introduces Nora-UI, teaches its features, and presents commonly used best practices accumulated from the Nora-UI community. Many examples are provided. Also, technical information on the internal structure of Nora-UI and recommended uses of Nora-UI are provided.</p>
<p>Test automation has specific advantages for improving the long-term efficiency of a software team's testing processes. Test automation supports:
<ul>
<li>Frequent regression testing</li>
<li>Rapid feedback to developers</li>
<li>Virtually unlimited iterations of test case execution</li>
<li>Support for Agile and extreme development methodologies</li>
<li>Disciplined documentation of test cases</li>
<li>Customized defect reporting</li>
<li>Finding defects missed by manual testing</li>
</ul>
</p>
<p>It is not always advantageous to automate test cases. There are times when manual testing may be more appropriate. For instance, if the application's user interface will change considerably in the near future, then any automation might need to be rewritten anyway. Also, sometimes there simply is not enough time to build test automation. For the short term, manual testing may be more effective. If an application has a very tight deadline, there is currently no test automation available, and it's imperative that the testing get done within that time frame, then manual testing is the best solution.</p>
</div>
<hr id="concepts" style="border-color: teal; margin: 25px;"/>
<div class="w3-container w3-margin-top">
<h2><i class="fa fa-lightbulb-o"></i> Concepts</h2>
<h3>Page Object Pattern</h3>
<p>
<b>Page Object Pattern</b>, the term that <b>Selenium</b> users keep buzzing. Page object is a design pattern that can be implemented as a Selenium best practices and also in NoraUi. The functionality classes (Page/PageElement) in this design represent a logical relationship between the pages of the application. Steps, regarding to them, encapsulate behaviors using one or several Pages.
</p>
<p>
The <b>Page Object Pattern</b> represents the screens of your web app as a series of objects and encapsulates the components represented by a page.
It allows us to model the UI in our tests.
A page object is an object-oriented class that serves as an interface to a page of your application unit tests.
</p>
<h3>Layered architecture</h3>
<p>
NoraUi framework introduces some concepts (Step, Page, PageElement, Selectors, packages...) all useful at different levels of your project. So let's explain quickly the different layers of NoraUi structure.
</p>
<div class="w3-panel w3-border w3-light-grey">
<div>
<div class="w3-third w3-margin-left w3-center w3-right">
<div class="w3-card-4">
<img src="img/architecture/architecture.gif" alt="archetype" style="width: 100%">
<div class="w3-container w3-light-gray">
<small>The 4 NoraUi architecture layers</small>
</div>
</div>
</div>
<ul class="w3-ul">
<li>
<h4>Gherkin Scenario</h4>
<p>The Gherkin layer represents the functional actions to execute within your test. It is the highest abstraction level of your scenario.</p>
</li>
<li>
<h4>Steps</h4>
<p>Steps layer represents all the glue code that will be available to be used from Gherkin scenario features. All Steps classes are included under the 'steps' package. This is true for both your project and NoraUi framework. The Steps will contain all your tests behaviors (web driver actions, checks...) that you can split into different classes and sub-package related to your business needs.</p>
</li>
<li>
<h4>Pages</h4>
<p>Pages layer represents your target applications by defining their structures in PageElements that are used in step actions. All Pages classes can be distributed into differents 'page' sub-packages depending on the business domains they belong to. No behavior should be done in these classes except some minor validations. Behaviors are done in the upper layer (Steps).</p>
</li>
<li>
<h4>Selectors</h4>
<p>The last layer is the one which is the closest to remote applications. It contains versioned .ini files by application which define how to retrieve a DOM element using the upper layer (Pages) PageElements. Several solutions are implemented like search by xpaths, class, id...</p>
</li>
</ul>
</div>
</div>
To conclude this point, every implemented scenario will run the following flow for each line:<br/>
<div class="w3-panel w3-teal w3-center">
<h4 class="w3-opacity"><i class="fa fa-arrow-down"></i> Read the current Gherkin line</h4><h4 class="w3-opacity"><i class="fa fa-arrow-down"></i> Found the matching Steps method to run</h4><h4 class="w3-opacity"><i class="fa fa-arrow-down"></i> Get the concerned Page and/or PagesElement</h4><h4 class="w3-opacity"><i class="fa fa-arrow-down"></i> Retrieve linked selectors</h4><h4 class="w3-opacity"><i class="fa fa-arrow-up"></i> Go back up to the Steps layer to execute the action</h4>
</div>
<p>So the framework is built to be used beginning from the top layer (closer to functional/business needs) to the bottom one (closer to target applications structures).</p>
<p>
Enough of theory, lets get into practical implementation.
</p>
</div>
<hr id="installationInstructionsEclipse" style="border-color: teal; margin: 25px;"/>
<div class="w3-container w3-margin-top">
<h2><i class="fa fa-book"></i> Installation Instructions from Eclipse</h2>
<p>
NORA-UI is a Maven project, You can use <a href="/~https://github.com/NoraUi/noraui-archetype" target="_blank">Noraui Archetype</a> to create your robot. <br /> In this chapter we will see together how to create a new Nora-UI project.
</p>
<p>
<b>Archetype catalog configuration</b><br/>
Create or edit the <i>archetype-catalog.xml</i> file in your .m2 repository and the following content:
<br />
</p>
<pre class="language-markup"><code><?xml version="1.0" encoding="UTF-8"?>
<archetype-catalog
xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-catalog/1.0.0 http://maven.apache.org/xsd/archetype-catalog-1.0.0.xsd"
xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-catalog/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<archetypes>
<archetype>
<groupId>com.github.noraui</groupId>
<artifactId>noraui-archetype</artifactId>
<version>4.4.0.0</version>
<description>Maven archetype for use a NORA-UI project</description>
</archetype>
</archetypes>
</archetype-catalog></code></pre>
</p>
<p>
<b>On Eclipse:</b><br /> New > Project... > Maven > Maven Project > Next > Next<br /> Select Group Id: com.github.noraui, Artifact Id: noraui-archetype and Version: 4.4.4.0<br /> Next
<div class="w3-row-padding w3-center w3-margin-top">
<div class="w3-half">
<div class="w3-card-4">
<img src="img/archetype1.png" alt="archetype" style="width: 100%">
<div class="w3-container w3-light-gray">
<small>Using NoraUi archetype in new Maven project Eclipse wizard</small>
</div>
</div>
</div>
</div>
<div class="w3-row-padding w3-center w3-margin-top">
<div class="w3-half">
<div class="w3-card-4">
<img src="img/archetype.png" alt="archetype" style="width: 100%">
<div class="w3-container w3-light-gray">
<small>Using NoraUi archetype in new Maven project Eclipse wizard</small>
</div>
</div>
</div>
</div>
<p>Finish</p>
</p>
</div>
<div id="installationInstructionsCmd" class="w3-container w3-margin-top">
<h2><i class="fa fa-book"></i> Installation Instructions from Cmd</h2>
<p>
<pre class="language-markup"><code>mvn archetype:generate -B -DarchetypeGroupId=com.github.noraui -DarchetypeArtifactId=noraui-archetype -DarchetypeVersion=4.4.0.0 -DgroupId=com.your.company -DartifactId=robot -Dversion=0.0.1-SNAPSHOT -DinteractiveMode=false</code></pre>
</p>
<p>
or
</p>
<p>
<pre class="language-markup"><code>mvn archetype:generate -B -DarchetypeGroupId=com.github.noraui -DarchetypeArtifactId=noraui-archetype -DarchetypeVersion=4.4.0.0 -DgroupId=com.your.company -DartifactId=ux -Dversion=0.0.1-SNAPSHOT -DrobotName=UxRobot -DtargetApplicationUrl=https://noraui.github.io/demo/logogame/v3/#/login -DtargetApplicationTitle="Logo Game" -DtargetApplicationId=logogame -DtargetApplicationName=Logogame</code></pre>
</p>
</div>
<hr id="quickStartGuide" style="border-color: teal; margin: 25px;"/>
<div class="w3-container w3-margin-top">
<h2><i class="fa fa-graduation-cap"></i> Quick Start Guide</h2>
<h3>Create a new test scenario</h3>
<h4>1. Create a feature file</h4>
<p>In your <i>src/test/resources/steps</i> folder, add a new file with the extension .feature.<br/>Name it as the tag it will contain. This tag will be used after to launch a scenario. <br/>You can move your scenario in any folder structure below <i>/step</i> folder.</p>
<div class="w3-row-padding w3-center w3-margin-top w3-margin-bottom">
<div class="w3-third">
<div class="w3-card-4">
<img src="img/quickstart-1.1.png" alt="archetype" style="width: 100%">
<div class="w3-container w3-light-gray">
<small>Adding a new feature in your project</small>
</div>
</div>
</div>
</div>
The next step is to modify the <i>src/main/resources/scenarios.properties</i> configuration file in order to indicate the path to the feature file among existing ones.<br/>
This configuration file is used by NoraUi to map a tag and a path.
<div class="w3-row-padding w3-center w3-margin-top w3-margin-bottom">
<div style="width: 80%;">
<div class="w3-card-4">
<img src="img/quickstart-1.2.png" alt="archetype" style="width: 100%;">
<div class="w3-container w3-light-gray">
<small>Setting up scenarios.properties</small>
</div>
</div>
</div>
</div>
We can now go back to our feature and add some basic content and a new step.<br/>
Copy these few lines to MyNewFeature.feature.
<pre class="language-gherkin"><code>@MyNewFeature
Feature: My new feature
Scenario Outline: My new feature scenario
When I click on $mypackage.MyPage-myElement
Examples:
#DATA
|id|
|1|
#END</code></pre>
You can notice that we used "I click on $mypackage.MyPage-myElement" step which comes directly from NoraUI framework.<br/>
When you write a feature, you have 2 possibilities when searching for steps:
<ul>
<li>You can use an existing one (from your project or from NoraUi)</li>
<li>You can create a new one and then implement it</li>
</ul>
<div class="w3-panel w3-pale-blue w3-leftbar w3-rightbar w3-border-blue">
<p>Here is a piece of advice for Eclipse: there is a very useful Cucumber JVM Eclipse Plugin that you can install via "Install New Software" menu via this address: https://cucumber.io/cucumber-eclipse/update-site.<br/> You need add the cucumber nature to your Maven project. Open the contextual menu from your robot (Maven project) - right click - Configure > Convert as cucumber project... A cucumber icon will appears on my project. The plugin indicates for example, non-implemented steps via highlighting, or existing steps via auto-completion (Ctrl+Space) as you can see on the screenshot below. More information on /~https://github.com/cucumber/cucumber-eclipse</p>
<p>To use external libraries as NoraUi within your Gherkin files, in Eclipse preferences go to Cucumber > User Settings. Then add 'noraui' root package to your classpath. Now your are able to use generic steps (click, type, check, open a page...) from NoraUi dependency in your own project.</p>
</div>
<div class="w3-row-padding w3-center w3-margin-top w3-margin-bottom">
<div>
<div class="w3-card-4">
<img src="img/quickstart-1.3.png" alt="archetype" style="width: 100%">
<div class="w3-container w3-light-gray">
<small>Example of auto-completion for existing steps</small>
</div>
</div>
</div>
</div>
You can find all methods <a href="https://noraui.github.io/NoraUi/com/github/noraui/application/steps/CommonSteps.html" target="_blank">here</a><br/><br/>
Let's focus now on the structure of the added steps within our new scenario.<br/>
<div class="w3-margin-left">
<b>When I click on $<span class="w3-text-red">mypackage.</span><span class="w3-text-green">MyPage</span><span class="w3-text-blue">-myElement</span>'</b>
</div>
The above step allows you to click on a specific element of a web page. It applies for any <span class="w3-text-green">Page</span>, for any of its <span class="w3-text-blue">PageElement</span> from any <span class="w3-text-red">'pages' subpackages</span> of your project.<br/>
<p>Then we want to continue on writing a more complex scenario in MyFeature.feature. So let's go back to it and complete the existing steps. <br/><b>The objective will be to do a simple google research and check the results to find the first link.</b><br/>The Gherkin content will look like this:
<pre class="language-gherkin"><code>@MyNewFeature
Feature: My new feature
Scenario Outline: My new feature scenario
Given 'GOOGLE_HOME' is opened.
# When I update text $google.SearchPage-searchField with '<search>'
# And I click on $google.SearchPage-searchButton
# The 2 above lines have been replaced due to google search page behavior change
When I update text $google.SearchPage-searchField and type ENTER with '<search>'
Then I check that $google.SearchPage-googleLink is present
And I go back to 'GOOGLE_HOME'
Examples:
#DATA
|id|search|Result|
|1|test|
#END</code></pre>
<h4>2. Create a Page class</h4>
<p>In order to reference target Page elements, we need to instantiate a new SearchPage in a new google package (under <i>application.pages</i>).</p>
<p>Paste the following Java code into SeachPage class:</p>
<pre class="language-java"><code>public class SearchPage extends Page {
public final PageElement searchField = new PageElement("-searchField", "Search field");
public final PageElement searchButton = new PageElement("-searchButton", "Search button");
public final PageElement googleLink = new PageElement("-googleLink", "Google first link");
public SearchPage() {
super();
this.application = "google";
this.pageKey = "GOOGLE_SEA";
this.callBack = Context.getCallBack("RESTART_WEB_DRIVER");
}
@Override
public boolean checkPage(Object... elements) {
return true;
}
}</code></pre>
<p>Your application pages must always extend abstract class <b>Page</b>.<br/>
From top to bottom, several instances of <b>PageElement</b> are created to make the link Gherkin feature. <br/>The constructor defines the application this Page comes from (we will see after how to define applications in <b>XXXContext</b> class), its unique key and a callback in case of failure of any behavior related to this Page. <br/>Finally the <b>checkPage()</b> method can be override to use your own way to validate the Page is correctly opened in the web browser driver (by checking current page title for instance).
</p>
<h4>3. Define selectors</h4>
<p>Once your new Page implemented, the next action consists of defining the way to select its PageElements when it is targeted by the web driver.<br/>
There are several ways to look for a HTML markup in a DOM in NoraUi (<b>CSS selector, link content, id, name, CSS class </b>or<b> xpath</b> which is the most complete mode but quite more difficult to describe).</p>
<p>
Under <i>src/main/resources/selectors/V1</i>, create a new file named google.ini and paste the following content:
</p>
<pre class="language-markup"><code>[GOOGLE_SEA-searchField]
id=lst-ib
[GOOGLE_SEA-searchButton]
name=btnK
[GOOGLE_SEA-googleLink]
xpath=(//h3//a)[1]</code></pre>
<p>
All you have to know about selectors is here. We easily locate searchField and searchButton using id and name HTML attributes. <br/>For the googleLink element, we use xpath to get the first link displayed on the results page.
</p>
<h4>4. Define your own steps</h4>
<p><b>You should use as much as possible the NoraUi generic steps for basic purposes.</b><br/>
This can be made directly with Gherkin language as we have done before, or within your own steps. Steps definition are simple classes under <i>application.steps</i> package. These classes should extend <b>Step</b> class. From them, you can use Pages and code specific actions and expectations:
</p>
<pre class="language-java"><code>public class MyOwnSteps extends Step {
@Inject
private SearchPage searchPage;
@Then("The GOOGLE search page is displayed")
public void checkGoogleSearchPage() throws FailureException {
if (!searchPage.checkPage()) {
new Result.Failure<>("google", "ERROR_MESSAGE", true, searchPage.getCallBack());
}
}</code></pre>
In this sample, we retrieve the <b>SearchPage</b> from <b>Page factory</b> and use it after in an <b>Gherkin exposed</b> (@Then...) public method to do a simple validation. If the validation fails, a new <b>Result.Failure</b> object is created to manage error reporting, screenshooting and callback invocation (we will see error management and reporting in incoming parts).
<p>You can go further by combining NoraUi generic methods calls inside one Step method.<br/>
Example:
<ul>
<li>You need click on button A.</li>
<li>Check value on input text B.</li>
<li>Update value on input text C with "GREEN" if value of B is different of "RED".</li>
<li>Or update value on input text C with "ERROR".</li>
</ul>
</p>
Create a new method in your code that will simply call existing generic methods of the framework as below:
<pre class="language-java"><code>@Given("custom application action with click on {page-element}, check value on {page-element} and update value on {page-element}")
public void customApplicationAction(PageElement elementNameA, PageElement elementNameB, PageElement elementNameC) {
clickOn(elementNameA);
if (!checkInputText(elementNameB, "RED")) {
updateText(elementNameC, "GREEN");
} else {
updateText(elementNameC, "ERROR");
}
}</code></pre>
<b>"clickOn"</b>, <b>"checkInputText"</b> and <b>"updateText"</b> already exist. You can find all methods <a href="https://noraui.github.io/NoraUi/noraui/application/steps/Step.html" target="_blank">here</a>
<p>Then from the Gherkin feature file, you just need to call your code like this:</p>
<pre class="language-gherkin"><code>@MyNewFeature
Feature: My new feature
Scenario Outline: My new feature scenario
...
custom application action with click on $package.Page-elementA, check value on $package.Page-elementB and update value on $package.Page-elementC
...
Examples:
#DATA
|id|
|1|
#END</code></pre>
<p>Sometimes you have no choice but to develop by yourself complex methods.
<ul>
<li>You need to send a flow request (SOAP, REST call...) and wait a possible response</li>
<li>You need check a special behavior.</li>
<li>Browse DOM arrays and objects serialized to json.</li>
<li>...</li>
</ul>
</p>
The principle stays the same:
<pre class="language-java"><code>@Then("custom application action with {string}")
public void cancelOrNotAllSteps(String fooJsonString) throws FailureException {
Foos foos = new Foos();
foos.deserialize(fooJsonString);
try {
for (Foo foo : foos) {
List<WebElement> rows = Context.waitUntil(ExpectedConditions.presenceOfAllElementsLocatedBy(Utilities.getLocator(myappHomePage.cancelStepList)));
if ("to cancel".equals(foo.getActionOfStepToBeProcessed())) {
for (int i = 0; i < rows.size(); i++) {
WebElement row = rows.get(i);
if (row.getText().startsWith(foo.getStepToBeProcessed())) {
selectCheckbox(myappHomePage.cancelStepCheckbox, true, i + 1);
}
}
} else if ("to achieve".equals(foo.getActionOfStepToBeProcessed())) {
for (int i = 0; i < rows.size(); i++) {
WebElement row = rows.get(i);
if (row.getText().startsWith(foo.getStepToBeProcessed())) {
selectCheckbox(myappHomePage.cancelStepCheckbox, false, i + 1);
}
}
}
}
} catch (TechnicalException e) {
new Result.Failure<>(fooJsonString, NoraRobotMessages.FAIL_MESSAGE_CLICK_ON_MY_APPLICATION, true, myappHomePage.getCallBack());
}
}</code>
</pre>
<p>
In our google search page testing scenario, we don't need to create new Steps methods for the moment. We will only use existing framework ones.
</p>
<h4>5. Manage your applications</h4>
<p>
Open your XXXContext.java class and add the following code in it (see lines with '/** Added line(s) **/' comment):
</p>
<pre class="language-java"><code>...
/** Added lines **/
public static final String GOOGLE_HOME = "GOOGLE_HOME";
public static final String GOOGLE_KEY = "google";
private String googleHome; // GOOGLE home url</b>
/**
* Constructor is useless because all attributes are static
*/
private NoraRobotContext() {
super();
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void initializeRobot(Class clazz) {
super.initializeRobot(clazz);
logger.info("NoraRobotContext > initializeRobot()");
// Urls configuration
/** Added line or use the NORAUI CLI directly **/
googleHome = getProperty(GOOGLE_KEY, applicationProperties);
logogameHome = getProperty(LOGOGAME_KEY, applicationProperties);
// Selectors configuration
/** Added line or use the NORAUI CLI directly **/
initApplicationDom(clazz.getClassLoader(), selectorsVersion, GOOGLE_KEY);
initApplicationDom(clazz.getClassLoader(), selectorsVersion, LOGOGAME_KEY);
// Exception Callbacks
/** Added line or use the NORAUI CLI directly **/
exceptionCallbacks.put(GO_TO_GOOGLE_HOME, STEPS_BROWSER_STEPS_CLASS_QUALIFIED_NAME, GO_TO_URL_METHOD_NAME, GOOGLE_HOME);
/** Added line or use the NORAUI CLI directly **/
exceptionCallbacks.put(CLOSE_WINDOW_AND_SWITCH_TO_GOOGLE_HOME, STEPS_BROWSER_STEPS_CLASS_QUALIFIED_NAME, "closeWindowAndSwitchTo", GOOGLE_KEY, GOOGLE_HOME);
/** Added line or use the NORAUI CLI directly **/
exceptionCallbacks.put(CLOSE_ALL_WINDOWS_AND_SWITCH_TO_GOOGLE_HOME, STEPS_BROWSER_STEPS_CLASS_QUALIFIED_NAME, "closeAllWindowsAndSwitchTo", GOOGLE_KEY);
exceptionCallbacks.put(GO_TO_LOGOGAME_HOME, STEPS_BROWSER_STEPS_CLASS_QUALIFIED_NAME, GO_TO_URL_METHOD_NAME, LOGOGAME_HOME);
exceptionCallbacks.put(CLOSE_WINDOW_AND_SWITCH_TO_LOGOGAME_HOME, STEPS_BROWSER_STEPS_CLASS_QUALIFIED_NAME, "closeWindowAndSwitchTo", LOGOGAME_KEY, LOGOGAME_HOME);
exceptionCallbacks.put(CLOSE_ALL_WINDOWS_AND_SWITCH_TO_LOGOGAME_HOME, STEPS_BROWSER_STEPS_CLASS_QUALIFIED_NAME, "closeAllWindowsAndSwitchTo", LOGOGAME_KEY);
// applications mapping
/** Added line or use the NORAUI CLI directly **/
applications.put(GOOGLE_KEY, new Application(GOOGLE_HOME, googleHome));
applications.put(LOGOGAME_KEY, new Application(LOGOGAME_HOME, logogameHome));
Page.setPageMainPackage("com.your.company.application.pages.");
}
...
// home getters
/** Added block or use the NORAUI CLI directly **/
public String getGoogleHome() {
return googleHome;
}
public String getLogogameHome() {
return logogameHome;
}
</code></pre>
<p>
Here, we just have defined in the robot Context a new "google" application linked with a selectors file named "google.ini" and pointing at a URI named "google" in the robot properties file (<i>src/main/resources/XXX.properties</i>).<br/>
So, in this file, add the following content: <b>google=${google}</b> and in <i>src/test/resources/environments</i> filter files, you can simply set a value to the new <b>google</b> variable. An environment filter file should look like this (for instance 'dev.properties'):
</p>
<pre class="language-gherkin"><code>#chrome, firefox or ie
browser=chrome
# remoteWebDriverUrl=http://localhost:4444/wd/hub
# remoteWebDriverBrowserVersion=80
# remoteWebDriverPlatformName=Linux
headless=true
noSandbox=true
# modifyheaderPath=
timeout=60
selectors.version=V1
display.stacktrace=false
#application list
logogame=https://noraui.github.io/demo/logogame/v3/
google=https://www.google.com/
# proxy configuration
http_proxy=
https_proxy=
no_proxy=
locale=en</code></pre>
<p>
These different filter files are necessary to run your scenarios on several platforms and manage the global configuration dynamically depending on the phase in which the robot is running. Commonly, the phases are <b>dev</b> for development, <b>ci</b> for continuous integration and <b>prod</b> for production if the robot aims for executing operations on applications in production.
</p>
<h4>6. Choose data providers</h4>
<p>
Before launching the test, we need to define one last thing, input and output data providers. <br/>
<b>Input data providers</b> are plugged to provide a data set on which the scenario will loop as many times as the lines it contains.<br/>
Several types are available: EXCEL, CSV, DB (ORACLE, MYSQL, POSTGRE), REST, GHERKIN. You can also create a specific one regarding your needs (see <a href="#customDataProvider">Custom data provider</a>).<br/>
<b>Output data providers</b> are mainly used to write results of each run on the provided data set. Other information can also be returned to the user during the execution (for instance a value read on a page element).<br/>
Several types are available: EXCEL, CSV, REST, CONSOLE. You can also create a specific one regarding your needs (see <a href="#customDataProvider">Custom data provider</a>).
</p>
<p>
For the rest of this exercise, we will configure GHERKIN as the input data provider and CONSOLE as the output data provider.<br/>That means testing data will be included directly within the 'MyNewFeature' feature file and results will be written in the default console.
</p>
<p>
Update your XXXRobot.properties and change these lines to set providers as described above:
</p>
<pre class="language-gherkin"><code>...
# type of input dataProvider (EXCEL, CSV, DB, REST, GHERKIN, noraui.data.xxx.YourCustomInputDataProvider)
dataProvider.in.type=GHERKIN
# type of output dataProvider (EXCEL, CSV, REST, CONSOLE, noraui.data.xxx.YourCustomOutputDataProvider)
dataProvider.out.type=CONSOLE
...</code></pre>
<p>
We are now ready to run our first test.
</p>
<h3>Run the scenario<h3>
<h4>1. From Eclipse run configuration</h4>
<p>
If you initialized the maven project by using the archetype, you should already have an available run configuration in Eclipse. Otherwise, it should look like this:
</p>
<div class="w3-row-padding w3-center w3-margin-top w3-margin-bottom">
<div class="w3-half">
<div class="w3-card-4">
<img src="img/quickstart-2.1.png" alt="Eclipse run configuration" style="width: 100%">
<div class="w3-container w3-light-gray">
<small>Eclipse run configuration</small>
</div>
</div>
</div>
</div>
<p>
Replace "norarobot" by the name of your project.<br/>
Then by running this configuration, a prompt will appear asking for the name of the feature to launch (exactly the <b>@tag</b> associated to the feature file on the top of it). Type "MyNewFeature" and the scenario is starting.
</p>
<h4>2. From Maven command</h4>
<p>
If you want to run you tests from a command prompt, the Eclipse run configuration equivalent would be:
</p>
<pre class="language-gherkin"><code>mvn clean verify -PscenarioInitiator,dev,norarobot -Dcucumber.options="--tags '@MyNewFeature'" -Dmaven.test.failure.ignore=true</code></pre>
<h3>Analyse results</h3>
<p>
As the input data are placed in <i>src/test/resources/data/in</i>, result files are placed in <i>src/test/resources/data/out</i> in the case you are using files as output (EXCEL or CSV). <br/>
For this tutorial, we chose the CONSOLE data output provider. So after the run, you should see the following content in the console:
</p>
<pre class="language-gherkin"><code>[2017-09-24 13:08:43]: > When I update text $google.SearchPage-searchField with 'test'
[2017-09-24 13:08:43]: #1 - When I click on $google.SearchPage-searchButton
[2017-09-24 13:08:44]: #2 - And I check that $google.SearchPage-googleLink is present
[2017-09-24 13:08:44]: #3 - And I go back to 'GOOGLE_HOME'
[2017-09-24 13:08:47]: ****************************************************************************************
[2017-09-24 13:08:47]: * *
[2017-09-24 13:08:47]: * Scenario: [@MyNewFeature] step 1 of 1 with 0 error(s) and 0 alert(s). 0s remaining *
[2017-09-24 13:08:47]: * *
[2017-09-24 13:08:47]: ****************************************************************************************
1 Scenarios (1 passed)
5 Steps (5 passed)
0m10,508s</code></pre>
<p>
In addition to providers, Cucumber generates 3 types of reports that you can find in <i>target/reports</i> folder. The HTML report is more appropriate for the majority of users. Json and Junit reports can be reinjected thereafter in other applications like dashboards or continuous integration tools (Jenkins, Travis CI...).
</p>
<h3>To go further</h3>
<p>Take a look at <a href="/~https://github.com/NoraUi/noraui-academy" target="_blank">nora-academy</a> project to create a new testing project on a local JHipster application.
</p>
</div>
<hr id="commandLineInterface" style="border-color: teal; margin: 25px;"/>
<div class="w3-container">
<h2><i class="fa fa-terminal"></i> Command Line Interface</h2>
<p>Nora-UI framework a CLI (Command Line Interface) for developers.</p>
<p>You can add a new target application, add a new scenario, ... using a command line.</p>
<div class="w3-card white">
<div class="w3-container w3-theme">
<h3>Command Line Interface</h3>
<p>
<img style="width: 30%" src="img/cli.png" alt="cli"/>
</p>
</div>
<ul class="w3-ul w3-border-top">
<li>
<h3>Add a new target application</h3>
<p>
<img style="width: 30%" src="img/cli-1.png" alt="Add a new target application"/>
</p>
</li>
<li>
<h3>Add a new scenario</h3>
<p>
<img style="width: 30%" src="img/cli-2.png" alt="Add a new scenario"/>
</p>
</li>
<li>
<h3>Add new model</h3>
<p>
<img style="width: 30%" src="img/cli-3.png" alt="Add new model"/>
</p>
</li>
<li>
<h3>Remove application</h3>
<p>
<img style="width: 30%" src="img/cli-4.png" alt="Remove application"/>
</p>
</li>
<li>
<h3>Remove scenario</h3>
<p>
<img style="width: 30%" src="img/cli-5.png" alt="Remove scenario"/>
</p>
</li>
<li>
<h3>Remove model</h3>
<p>
<img style="width: 30%" src="img/cli-6.png" alt="Remove model"/>
</p>
</li>
<li>
<h3>Encrypt data</h3>
<p>
<img style="width: 30%" src="img/cli-7.png" alt="Encrypt data"/>
</p>
</li>
<li>
<h3>Decrypt data</h3>
<p>
<img style="width: 30%" src="img/cli-8.png" alt="Decrypt data"/>
</p>
</li>
</ul>
</div>
</div>
<hr id="javadoc" style="border-color: teal; margin: 25px;"/>
<div class="w3-container">
<h2><i class="fa fa-book"></i> Official java doc</h2>
<p>Nora-UI framework has a complete technical documentation for developers.</p>
<p>You can find all official java doc <a href="https://noraui.github.io/NoraUi/" target="_blank">here</a></p>
</div>
<hr id="continuousIntegration" style="border-color: teal; margin: 25px;"/>
<div class="w3-container">
<h2><i class="fa fa-cogs"></i> Continuous Integration</h2>
<p>The use of Nora-UI is achieved through continuous integration.</p>
<p>In this chapter we will see together how to configure a Jenkins and reap the results on a daily basis.</p>
<ul>
<li>Jenkins</li>
<p>Use a Maven job.</p>
<p style="margin:0">On "Goal" of <b>Build</b> part:</p>
<p style="margin:0"> clean test javadoc:javadoc -Dcucumber.options="--tags '@LoginLogout'" -Pjavadoc,preIC,scenarioInitiator,ci,unit-tests,postIC,drivers,analyze -Dmaven.test.failure.ignore=true sonar:sonar</p>
<p style="margin-bottom:0">On "Execute shell script" of <b>Post Steps</b> part:</p>
<p style="margin:0"> cp ${WORKSPACE}/src/test/resources/dev/formatter.js ${WORKSPACE}/target/reports/html/formatter.js</p>
<p style="margin:0"> cp ${WORKSPACE}/src/test/resources/dev/index.html ${WORKSPACE}/target/reports/html/index.html</p>
<p style="margin:0"> cp ${WORKSPACE}/src/test/resources/dev/counter.js ${WORKSPACE}/target/reports/html/counter.js</p>
<p style="margin-bottom:0">On "Archive artifacts" of <b>Actions following the build</b> part:</p>
<p style="margin:0"> target/classes/**/*,target/coverage-reports/*</p>
<p style="margin-bottom:0">On "Reports" of <b>Publish HTML reports</b> part (from <a href="https://wiki.jenkins.io/display/JENKINS/HTML+Publisher+Plugin" target="_blank">HTML Publisher plugin</a>):</p>
<p style="margin:0"> HTML directory to archive: target/reports/html</p>
<p style="margin:0"> Index page[s]: index.html</p>
<p style="margin-top:0"> Report title: Cucumber HTML Report</p>
<img style="width: 210px" src="img/cucumber-html-report-link.gif" alt="cucumber-html-report-link"/><br/><br/>
<img style="width: 50%" src="img/cucumber-html-report.gif" alt="cucumber-html-report"/><br/><br/>
<p style="margin:0">On "Groovy script" of <b>Groovy Postbuild</b> part (from <a href="https://wiki.jenkins.io/display/JENKINS/Groovy+Postbuild+Plugin" target="_blank">Groovy Postbuild</a>):</p>
<pre class="language-java"><code>
if(expectationMatcher.count >0){
def utCount = manager.build.logFile.text.count("Failures: 0, Errors: 0, Skipped: 0")
String expectation = expectationMatcher[0][1]
def expectationCount = manager.build.logFile.text.count(expectation)
if(!(expectationCount==2 && (manager.envVars["UT_COUNT"].equals(utCount+"") || utCount == 2))) {
manager.addErrorBadge("Tests results don't match expectations")
manager.createSummary("error.gif").appendText("<h1>Wrong tests results</h1></br>Expected: " + expectation + "<br/>Expected successful UT: " + manager.envVars["UT_COUNT"] + " and result is " + utCount, false, false, false, "red")
manager.buildFailure()
}
else{
manager.addBadge("success.gif", "Tests result match expectations")
manager.createSummary("green.gif").appendText("<h1>Build instable but errors match expectations</h1></br>Expected: " + expectation + "<br/>Successful UT: " + utCount, false, false, false, "green")
}
}</code></pre>
<br/>
<li>Travis CI</li>
<p style="margin:0">On "travis.yml" of <b>Build</b> part:</p>
<p style="margin:0"> script:</p>
<p style="margin:0"> - test/run.sh</p>
<p style="margin-bottom:0">On <b>test/run.sh</b> file:</p>
<p style="margin:0"> mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package javadoc:javadoc -Dcucumber.options="--tags '@LoginLogout'" -Pjavadoc,preIC,scenarioInitiator,ci,unit-tests,postIC,drivers,analyze -Dmaven.test.failure.ignore=true sonar:sonar -Dsonar.host.url=https://sonarqube.com -Dsonar.organization=$YOUR_ORGANIZATION -Dsonar.login=$SONAR_TOKEN</p>
<p style="margin:0"> curl -s "https://api.travis-ci.org/jobs/${TRAVIS_JOB_ID}/log.txt?deansi=true" > nonaui.log</p>
<p style="margin:0"> sed ... and exit 255 if an error occurred.</p>
</ul>
</div>
<hr id="zap" style="border-color: teal; margin: 25px;"/>
<div class="w3-container">
<h2><i class="fa fa-bolt"></i> OWASP Zed Attack Proxy (ZAP)</h2>
<p>Nora-UI framework can be use to OWASP Zed Attack Proxy</p>
<p>You can find all official doc <a href="https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project" target="_blank">here</a> and you can download ZAP installer <a href="https://www.zaproxy.org/download/" target="_blank">here</a></p>
<p>Nora-UI configuration (robot\src\test\resources\environments\dev.properties):<pre class="language-gherkin"><code># proxy configuration
http_proxy=http://localhost:8087
https_proxy=http://localhost:8087
no_proxy=</code></pre><br/>Start Zed Attack Proxy (ZAP) on a particular address and port. ZAP provides a setting for that: Open Options and then Local Proxies and specify your proxy settings.<br/>
<img style="width: 50%" src="img/zap-local-proxies.png" alt="zap-local-proxies"/>
<br/>Besides that it should be possible to configure the proxy used by ZAP like you would do for any other Java based application. ZAP provides a setting for that: Open Options and then Connection and specify your proxy settings.<br/>
<img style="width: 50%" src="img/zap-proxy.png" alt="zap-proxy-yourcompany"/>
<p>
</div>
<hr id="customDataProvider" style="border-color: teal; margin: 25px;"/>
<div class="w3-container">
<h2><i class="fa fa-database"></i> Custom data provider</h2>
<p>You can create a custom data provider (Java language).</p>
<ul>
<li>Create a new package « xxx.xxx.xxx.data.xxx » (Example: « com.your.company.data.json »).</li>
<li>Create a new java class (Example: « CustomJsonDataProvider »).</li>
<li>Make this this class extend “CommonDataProvider » and implement « DataInputProvider » and/or « DataOutputProvider ».</li>
<li>Redefine the mandatory unimplemented methods (you can refer to the NoraUi existing ones).</li>
<li>In your XXXRobot.properties file:</li>
</ul>
<pre class="language-gherkin"><code># type of input dataProvider (EXCEL, CSV, SQL, REST, GHERKIN, com.your.company.data.xxx.YourCustomInputDataProvider)
dataProvider.in.type=com.your.company.data.json.CustomJsonDataProvider
# type of output dataProvider (EXCEL, CSV, REST, CONSOLE, com.your.company.data.xxx.YourCustomOutputDataProvider)
dataProvider.out.type=com.your.company.data.json.CustomJsonDataProvider</code></pre>
<p>You can find a sample <a href="/~https://github.com/NoraUi/noraui.github.io/blob/master/howToUse/CustomJsonDataProvider.java" target="_blank">here</a></p>
</div>
<hr id="ourClients" style="border-color: teal; margin: 25px;"/>
<div class="w3-container">
<h2><i class="fa fa-thumbs-o-up"></i> Our clients</h2>
<p>They trust us.</p>
<p class="w3-center"><img src="img/clients.png" alt="Our clients"/></p>
</div>
<!-- Footer -->
<hr style="border-color: teal; margin: 25px;"/>
<footer class="w3-container w3-padding-16 w3-light-grey">
<h4>NORA-UI</h4>
<p>
<small>Powered by <a href="/~https://github.com/NoraUi" target="_blank">NoraUi Organization</a> only.</small> <i class="fa fa-github"></i> NoraUi is open-source and completely free.
</p>
<div style="position: relative; bottom: 55px;" class="w3-tooltip w3-right">
<span class="w3-text w3-theme-light w3-padding">Go To Top</span> <a class="w3-text-white" href="#"><span class="w3-xlarge"> <i class="fa fa-chevron-circle-up"></i></span></a>
</div>
<p>
<small>Copyright © 2023 <a href="/~https://github.com/NoraUi" target="_blank">NoraUi Organization</a> All rights reserved.</small>
</p>
</footer>
<!-- End page content -->
</div>
<script>
// Get the Sidebar
var mySidebar = document.getElementById("mySidebar");
// Get the DIV with overlay effect
var overlayBg = document.getElementById("myOverlay");
// Toggle between showing and hiding the sidebar, and add overlay effect
function w3_open() {
if (mySidebar.style.display === 'block') {
mySidebar.style.display = 'none';
overlayBg.style.display = "none";
} else {
mySidebar.style.display = 'block';
overlayBg.style.display = "block";
}
}
// Close the sidebar with the close button
function w3_close() {
mySidebar.style.display = "none";
overlayBg.style.display = "none";
}
function goTo(el) {
var menuItems = document.getElementsByClassName("menu-item");
for(var i = 0; i<menuItems.length; i++){
menuItems[i].className = menuItems[i].className.replace(/w3-teal/, "");
}
el.className += " w3-teal";
w3_close();
}
</script>
</body>
</html>