nxgreport 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/lib/nxgcore.rb +59 -381
  3. data/lib/nxgcss.rb +62 -10
  4. data/lib/nxghtml.rb +196 -0
  5. data/lib/nxgjs.rb +233 -0
  6. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a070244e38842f494942ea189deba31e45360a50857946d17c11749ab10db465
4
- data.tar.gz: f903e4695cffc3a15069340f563e2ed7930d1531139ff7a4b6219786e8452649
3
+ metadata.gz: bfe47cd8feccee393041f0b98c2c06fbf0d74c646f68de93d3fbd8fa3d781c54
4
+ data.tar.gz: 3c7614aa31becf095ef9700270ad71966aab0c1a651e6c7723b32d8a2958a2b2
5
5
  SHA512:
6
- metadata.gz: d65497235406123d769ac72f0f61b1a16ca565926911d34819ddcd8d5ea730f2363d969212e372613a9457d97a1649ed8711fc15c75ee0c619a1d34d804adec8
7
- data.tar.gz: 8eb082850d2be42ef630e83e23a68e1058182ab26ee84f8738b17d8f8964b5e45de460ee05d905fe8e15bbde41f577348055457f5da00f416b2cf542064cdc17
6
+ metadata.gz: 2d0980043126d503c24faade62e33e0e335ae4fa85f47b9d7d02f95b9794a6f8315879c43154a1b2c3e2b4b7a43b6de021b9405981cd7ba38e8399884ffe30a3
7
+ data.tar.gz: 6f21ccc9e46747d3679a7d8c4d36b6d6e2c8eb9525b39fe2e10a18808695da3f62a34557ad0aea547dc66a6860342bfc42ad0f4ca40db6cd741398ffec00b894
@@ -1,28 +1,27 @@
1
1
  require 'fileutils'
2
- require 'pry'
3
- require 'json'
4
- require 'nxgcss.rb'
2
+ require 'nxghtml.rb'
5
3
 
6
4
  class NxgCore
7
5
  class NxgReport
8
6
 
9
- include NxgCss
7
+ include NxgHTML
10
8
 
11
9
  def initialize(data_provider)
12
10
  @data_provider = data_provider
13
11
  @data_provider[:pass] = 0
14
12
  @data_provider[:fail] = 0
15
13
  @data_provider[:total] = 0
14
+ @data_provider[:open_on_completion] = false
15
+ @data_provider[:features] = Array.new()
16
+ @data_provider[:title] = ""
17
+ @data_provider[:report_path] = ""
18
+ @start_time = Time.now.to_f
19
+ @test_start_time = Time.now.to_f
16
20
  end
17
21
 
18
- def setup(location: "./NxgReport.html", title: "Features Summary")
22
+ def setup(location: "./NxgReport.html", title: "$NxgReport")
19
23
  @data_provider[:report_path] = location.empty? ? "./NxgReport.html" : location
20
- folder_check()
21
24
  @data_provider[:title] = title
22
- @data_provider[:title_color] = ""
23
- @data_provider[:open_on_completion] = false
24
- @data_provider[:features] = Array.new()
25
- @start_time = Time.now.to_f
26
25
  end
27
26
 
28
27
  def open_upon_execution(value: true)
@@ -67,8 +66,28 @@ class NxgCore
67
66
 
68
67
  @data_provider[:execution_date] = date
69
68
  end
69
+
70
+ def set_execution_time()
71
+ time_diff_in_mins = 0
72
+ time_diff_in_secs = 0
73
+
74
+ @data_provider[:features].each do |feature|
75
+ feature["tests"].each do |test|
76
+ time_diff_in_secs += test["time"]
77
+ end
78
+ end
79
+
80
+ time_diff_in_mins = ((time_diff_in_secs) / 60).to_i
81
+
82
+ if time_diff_in_mins >= 60
83
+ time_diff_in_hrs = (time_diff_in_mins / 60.to_f).round(2)
84
+ @data_provider[:execution_time] = "#{time_diff_in_hrs} #{time_diff_in_hrs == 1 ? "hour" : "hours"}"
85
+ else
86
+ @data_provider[:execution_time] = "#{time_diff_in_mins} mins"
87
+ end
88
+ end
70
89
 
71
- def log_test(feature_name: "", test_name:"", test_status: "", comments: "")
90
+ def log_test(feature_name: "", test_name:"", test_status: "", tag: "", comments: "", execution_time: 0)
72
91
  if feature_name.nil?() || feature_name.strip.empty?()
73
92
  log("Feature name cannot be empty.")
74
93
  return
@@ -100,13 +119,16 @@ class NxgCore
100
119
  @data_provider[:features].push(new_feature)
101
120
  end
102
121
 
103
- update_feature(f_name, t_name, t_pass, t_comments)
122
+ update_feature(f_name, t_name, t_pass, t_comments, get_execution_time(execution_time), tag)
104
123
  @data_provider[:total] += 1
105
124
  @data_provider[t_pass ? :pass : :fail] += 1
106
125
  end
107
126
 
108
- def build(execution_time: 0)
109
- set_execution_time(execution_time)
127
+ def build()
128
+ @data_provider[:report_path] = generate_report_path() if @data_provider[:report_path].empty?()
129
+ @data_provider[:title] = "$NxgReport" if @data_provider[:title].empty?()
130
+ folder_check()
131
+ set_execution_time()
110
132
  write()
111
133
  if @data_provider[:open_on_completion]
112
134
  system("open #{@data_provider[:report_path]}") if File.file?(@data_provider[:report_path])
@@ -115,7 +137,7 @@ class NxgCore
115
137
 
116
138
  # Private methods
117
139
 
118
- def update_feature(f_name, t_name, t_pass, t_comments)
140
+ def update_feature(f_name, t_name, t_pass, t_comments, t_execution_time, t_tag)
119
141
  @data_provider[:features].each do |feature|
120
142
  if feature["name"].eql?(f_name)
121
143
  feature["total"]+=1
@@ -123,7 +145,9 @@ class NxgCore
123
145
  feature["tests"].push({
124
146
  "name" => t_name,
125
147
  "testPass" => t_pass,
126
- "comments" => t_comments
148
+ "comments" => t_comments,
149
+ "time" => t_execution_time,
150
+ "tag" => t_tag
127
151
  })
128
152
  return
129
153
  end
@@ -157,378 +181,32 @@ class NxgCore
157
181
  return
158
182
  end
159
183
  template = File.new(@data_provider[:report_path], 'w')
160
- template.puts("<html lang=\"en\">
161
- #{head()}
162
- #{body()}
163
- #{javascript()}
164
- </html>")
184
+ template.puts(html(@data_provider))
165
185
  template.close()
166
186
  end
167
187
 
168
- def head()
169
- "<head>
170
- <meta charset=\"UTF-8\" />
171
- <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />
172
- <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js\"></script>
173
- <title>Home | #{@data_provider[:title]}</title>
174
- #{google_fonts_link()}
175
- #{icons_link()}
176
- #{css(@data_provider)}
177
- </head>"
178
- end
179
-
180
- def google_fonts_link()
181
- "<link
182
- href=\"https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,600;0,700;0,800;1,300;1,400;1,600;1,700;1,800&display=swap\"
183
- rel=\"stylesheet\"
184
- />"
185
- end
186
-
187
- def icons_link()
188
- "<link
189
- href=\"https://fonts.googleapis.com/icon?family=Material+Icons\"
190
- rel=\"stylesheet\"
191
- />"
192
- end
193
-
194
- def body()
195
- "<body id=\"app\" onload=\"onRefresh()\">
196
- <div id=\"sidebar\" onclick=\"closeDetails()\">
197
- <div id=\"sidebar-div\">
198
- <div id=\"sidebar-title-wrap\">
199
- <h1 id=\"sidebar-title\">Title</h1>
200
- &nbsp;&nbsp;
201
- <i class=\"material-icons\" id=\"sidebar-status\">check_circle</i>
202
- </div>
203
- </div>
204
- </div>
205
- <div id=\"sidebar-overlay\" onclick=\"closeDetails()\">
206
- <div id=\"sidebar-overlay-grid\"></div>
207
- </div>
208
- <div id=\"body-wrap\">
209
- #{header()}
210
- #{config()}
211
- #{features()}
212
- #{footer()}
213
- </div>
214
- </body>"
215
- end
216
-
217
- def header()
218
- "<div id=\"header\">
219
- <h1 id=\"app-title\">#{@data_provider[:title]}</h1>
220
- <div id=\"theme-wrap\">
221
- <button id=\"theme-switch\" onclick=\"switchTheme()\">
222
- <i class=\"material-icons\" id=\"theme-icon\">brightness_2</i>
223
- </button>
224
- </div>
225
- </div>"
226
- end
227
-
228
- def features()
229
- "<div class=\"features-grid\"></div>"
230
- end
231
-
232
- def features_js_array()
233
- return @data_provider[:features].to_s.gsub("=>", ":")
234
- end
235
-
236
- def footer()
237
- "<div id=\"footer\">
238
- <p>
239
- Developed by
240
- <span>
241
- <a
242
- href=\"https://www.linkedin.com/in/balabharathijayaraman\"
243
- rel=\"nofollow\"
244
- target=\"_blank\"
245
- >Balabharathi Jayaraman</a
246
- >
247
- </span>
248
- </p>
249
- </div>"
250
- end
251
-
252
- def javascript()
253
- "<script>
254
- var darkTheme = false;
255
- var displayFailuresOnly = false;
256
-
257
- const allFeatures = #{features_js_array};
258
-
259
- var dataSource = [];
260
-
261
- const STATUS = {
262
- pass: \"check_circle\",
263
- fail: \"cancel\",
264
- };
265
-
266
- function onRefresh() {
267
- switchTheme();
268
- dataSource = allFeatures;
269
- setFilter();
270
- }
271
-
272
- function updateView() {
273
- $(\".banner\").removeClass(\"banner\").addClass(\"features-grid\");
274
- $(\".features-grid\").empty();
275
-
276
- if (dataSource.length === 0) {
277
- console.log(\"inside\");
278
- $(\".features-grid\")
279
- .removeClass(\"features-grid\")
280
- .addClass(\"banner\")
281
- .append(
282
- `<i class=\"banner-text green-font material-icons\">done_all</i><h1>No Failures</>`
283
- );
284
- return;
285
- }
286
-
287
- dataSource.forEach((feature, index) => {
288
- $(\".features-grid\").append(
289
- `<div class=\"module dark ${
290
- feature.fail > 0 ? \"red-bg\" : \"\"
291
- }\" onclick=\"showDetails(${index})\"><div class=\"funcname\"><h4>${
292
- feature.name
293
- }</h4></div><div class=\"total\"><h6>Total</h6><h4>${
294
- feature.total
295
- }</h4></div><div class=\"pass green-font\"><h6>Passed</h6><h4>${
296
- feature.pass
297
- }</h4></div><div class=\"fail red-font\"><h6>Failed</h6><h4>${
298
- feature.fail
299
- }</h4></div></div>`
300
- );
301
- });
302
- }
303
-
304
- function setFilter() {
305
- if (displayFailuresOnly) {
306
- $(\"#filter h5\").text(\"Failed\");
307
- dataSource = allFeatures.filter((feature) => {
308
- return feature.fail > 0;
309
- });
310
- } else {
311
- $(\"#filter h5\").text(\"All\");
312
- dataSource = allFeatures;
313
- }
314
- updateView();
315
- displayFailuresOnly = !displayFailuresOnly;
316
- }
317
-
318
- function filterAllFailed() {
319
- allFailedTests = [];
320
-
321
- failedFeatures = allFeatures.filter((feature) => {
322
- return feature.fail > 0;
323
- });
324
-
325
- for (index = 0; index < failedFeatures.length; index++) {
326
- failedFeatures[index].tests.filter((test) => {
327
- if (!test.testPass) {
328
- allFailedTests.push(test);
329
- }
330
- });
331
- }
332
-
333
- $(\"#sidebar-overlay\").css(\"visibility\", \"visible\");
334
- $(\"#sidebar-overlay\").css(\"margin-left\", \"40%\");
335
- $(\"#sidebar\").css(\"width\", \"40%\");
336
- $(\"#sidebar-title\").css(\"visibility\", \"visible\");
337
- $(\"#sidebar-status\").css(\"visibility\", \"visible\");
338
- /* Update Test Information */
339
-
340
- $(\"#sidebar-title\").text(\"Failed Tests\");
341
- $(\"#sidebar-status\").text(STATUS.fail);
342
- $(\"#sidebar-overlay-grid\").empty();
343
- allFailedTests.forEach((test) => {
344
- $(\"#sidebar-overlay-grid\").append(
345
- `<div id=\"sidebar-overlay-test-info\" onclick=\"()=>{}\"><i class=\"${
346
- test.testPass ? \"green-font\" : \"red-font\"
347
- } material-icons\" style=\"font-size: 1em\">${
348
- STATUS.fail
349
- }</i>&nbsp;&nbsp;<h4 id=\"test-title\">${test.name}</h4>${
350
- test.comments !== \"\"
351
- ? `<p id=\"error-message\">${test.comments}</p>`
352
- : \"\"
353
- }</div>`
354
- );
355
- });
356
- }
357
-
358
- function switchTheme() {
359
- $(document.documentElement).attr(\"theme\", !darkTheme ? \"light\" : \"dark\");
360
- $(\"#theme-icon\").text(!darkTheme ? \"wb_sunny\" : \"brightness_2\");
361
- darkTheme = !darkTheme;
362
- }
363
-
364
- function closeDetails() {
365
- $(\"#sidebar-title\").css(\"visibility\", \"hidden\");
366
- $(\"#sidebar-status\").css(\"visibility\", \"hidden\");
367
- $(\"#sidebar-overlay\").css(\"visibility\", \"hidden\");
368
- $(\"#sidebar-overlay\").css(\"margin-left\", \"0\");
369
- $(\"#sidebar\").css(\"width\", \"0\");
370
- }
371
-
372
- window
373
- .matchMedia(\"(prefers-color-scheme: dark)\")
374
- .addEventListener(\"change\", (e) => {
375
- darkTheme = e.matches;
376
- switchTheme();
377
- });
378
-
379
- function showDetails(featureID) {
380
- feature = dataSource[featureID];
381
-
382
- $(\"#sidebar-overlay\").css(\"visibility\", \"visible\");
383
- $(\"#sidebar-overlay\").css(\"margin-left\", \"40%\");
384
- $(\"#sidebar\").css(\"width\", \"40%\");
385
- $(\"#sidebar-title\").css(\"visibility\", \"visible\");
386
- $(\"#sidebar-status\").css(\"visibility\", \"visible\");
387
- /* Update Test Information */
388
-
389
- $(\"#sidebar-title\").text(feature.name);
390
- $(\"#sidebar-overlay-grid\").empty();
391
- feature.tests.forEach((test) => {
392
- $(\"#sidebar-overlay-grid\").append(
393
- `<div id=\"sidebar-overlay-test-info\" onclick=\"()=>{}\"><i class=\"${
394
- test.testPass ? \"green-font\" : \"red-font\"
395
- } material-icons\" style=\"font-size: 1em\">${
396
- test.testPass ? STATUS.pass : STATUS.fail
397
- }</i>&nbsp;&nbsp;<h4 id=\"test-title\">${test.name}</h4>${
398
- test.comments !== \"\"
399
- ? `<p id=\"error-message\">${test.comments}</p>`
400
- : \"\"
401
- }</div>`
402
- );
403
- });
404
-
405
- for (index = 0; index < feature.tests.length; index++) {
406
- if (!feature.tests[index].testPass) {
407
- $(\"#sidebar-status\").text(STATUS.fail);
408
- return;
409
- }
410
- }
411
- $(\"#sidebar-status\").text(STATUS.pass);
412
- }
413
-
414
- </script>"
415
- end
416
-
417
- def config()
418
- return if @data_provider.length == 0
419
-
420
- return "<div class=\"params-container\">
421
- #{release_name()}
422
- #{execution_date()}
423
- #{device()}
424
- #{os()}
425
- #{app_version()}
426
- #{environment()}
427
- #{passed_tests()}
428
- #{failed_tests()}
429
- #{percentage_pass()}
430
- #{execution_time()}
431
- #{filter()}
432
- </div>"
188
+ def generate_report_path()
189
+ report_filename = @data_provider.key?(:release_name) ? @data_provider[:release_name] : "NxgReport"
190
+ report_filename += "-#{@data_provider[:device]}" if @data_provider.key?(:device)
191
+ report_filename += "-#{@data_provider[:os]}" if @data_provider.key?(:os)
192
+ report_filename += "-#{@data_provider[:app_version]}" if @data_provider.key?(:app_version)
193
+ report_filename += "-#{@data_provider[:environment]}" if @data_provider.key?(:environment)
194
+ report_filename = report_filename.gsub(/[^0-9a-z-]/i, '')
195
+ return "./#{report_filename}.html"
433
196
  end
434
197
 
435
- def execution_time()
436
- return if !@data_provider.key?(:environment)
437
-
438
- return config_item("Total execution time", @data_provider[:execution_time],'access_time')
439
- end
440
-
441
- def filter()
442
- "<div class=\"param-wrap\" onclick=\"setFilter()\" id=\"filter\" title=\"Filter tests\">
443
- <i class=\"pi material-icons\">filter_list</i>
444
- <h5 id=\"pt\">Failed</h5>
445
- </div>"
446
- end
447
-
448
- def passed_tests()
449
- "<div class=\"param-wrap\" title=\"Passed tests\">
450
- <i class=\"pi green-font material-icons\">check_circle</i>
451
- <h5 id=\"pt\">#{@data_provider[:pass] == 0 ? "None" : @data_provider[:pass]}</h5>
452
- </div>"
453
- end
454
-
455
- def failed_tests()
456
- "<div class=\"param-wrap\" title=\"Failed tests\" #{@data_provider[:fail] > 0 ? "onclick=\"filterAllFailed()\" style=\"cursor: pointer\"" : ""}>
457
- <i class=\"pi red-font material-icons\">cancel</i>
458
- <h5 id=\"pt\">#{@data_provider[:fail] == 0 ? "None" : @data_provider[:fail]}</h5>
459
- </div>"
460
- end
461
-
462
- def percentage_pass()
463
- pass_percentage = ((@data_provider[:pass]/@data_provider[:total].to_f) * 100).round(2)
464
-
465
- return config_item("Pass percentage", pass_percentage,'equalizer')
466
- end
467
-
468
- def environment()
469
- return if !@data_provider.key?(:environment)
470
-
471
- return config_item("Test environment", @data_provider[:environment], "layers")
472
- end
473
-
474
- def app_version()
475
- return if !@data_provider.key?(:app_version)
476
-
477
- return config_item("App version tested", @data_provider[:app_version], "info")
478
- end
479
-
480
- def release_name()
481
- return if !@data_provider.key?(:release_name)
482
-
483
- return config_item("Release", @data_provider[:release_name], "bookmark")
484
- end
485
-
486
- def os()
487
- return if !@data_provider.key?(:os)
488
-
489
- return config_item("Os tested", @data_provider[:os], "settings")
490
- end
491
-
492
- def device()
493
- return if !@data_provider.key?(:device)
494
-
495
- return config_item("Device tested", @data_provider[:device], "devices")
496
- end
497
-
498
- def execution_date()
499
- @data_provider[:execution_date] = Time.now().strftime("%b %d, %Y") if !@data_provider.key?(:execution_date)
500
-
501
- return config_item("Execution date", @data_provider[:execution_date], "event")
502
- end
503
-
504
- def config_item(toot_tip, name, icon)
505
- "<div class=\"param-wrap\" title=\"#{toot_tip}\">
506
- <i class=\"pi material-icons\">#{icon}</i>
507
- <h5 id=\"pt\">#{name}</h5>
508
- </div>"
509
- end
510
-
511
- def set_execution_time(time)
512
- time_diff_in_mins = 0
513
- if time == 0
514
- @end_time = Time.now.to_f
515
- time_diff_in_mins = ((@end_time - @start_time) / 60).to_i
516
- else
517
- time_diff_in_mins = (time / 60).to_i
518
- end
519
-
520
- if time_diff_in_mins >= 60
521
- time_diff_in_hrs = (time_diff_in_mins / 60.to_f).round(2)
522
- @data_provider[:execution_time] = "#{time_diff_in_hrs} #{time_diff_in_hrs == 1 ? "hour" : "hours"}"
523
- else
524
- @data_provider[:execution_time] = "#{time_diff_in_mins} mins"
198
+ def get_execution_time(execution_time)
199
+ if execution_time != 0
200
+ @test_start_time = Time.now.to_f
201
+ return execution_time.round()
525
202
  end
203
+ @test_end_time = Time.now.to_f
204
+ execution_time = (@test_end_time - @test_start_time).round()
205
+ @test_start_time = Time.now.to_f
206
+ return execution_time
526
207
  end
527
-
528
- private :log, :clean, :write
529
- private :execution_date, :device, :os, :release_name, :app_version, :environment
530
- private :features, :config, :config_item, :features_js_array
531
- private :head, :body, :header, :footer, :javascript
208
+
209
+ private :log, :clean, :write, :update_feature, :folder_check
532
210
  end
533
211
 
534
212
  private_constant :NxgReport
@@ -14,21 +14,21 @@ module NxgCss
14
14
  --primary-color: #fff;
15
15
  --secondary-color: #fff;
16
16
  --primary-font-color: #424242;
17
- --green: rgb(31, 172, 31);
17
+ --green: rgb(19, 143, 19);
18
18
  --red: rgb(214, 7, 7);
19
19
  --blue: rgb(0, 89, 255);
20
- --red-bg: rgba(255, 0, 0, 0.233);
20
+ --red-bg: #ff073925;
21
21
  }
22
-
22
+
23
23
  [theme=\"dark\"] {
24
24
  --background-color: #252525;
25
25
  --primary-color: #2e2e2e;
26
- --secondary-color: rgba(255, 255, 255, 0.842);
26
+ --secondary-color: #fff;
27
27
  --primary-font-color: #f9fafccb;
28
- --green: rgb(6, 207, 6);
29
- --red: rgb(228, 1, 1);
30
- --blue: rgb(91, 226, 250);
31
- --red-bg: rgba(201, 53, 53, 0.479);
28
+ --green: #15ce40;
29
+ --red: #ff073a;
30
+ --blue: #2cbcff;
31
+ --red-bg: #ff07392c;
32
32
  }
33
33
 
34
34
  * {
@@ -71,6 +71,36 @@ module NxgCss
71
71
 
72
72
  #sidebar-div {
73
73
  margin: auto;
74
+ display: grid;
75
+ grid-template-rows: 1fr auto;
76
+ place-items: center;
77
+ }
78
+
79
+ #sidebar-catergories {
80
+ width: 80%;
81
+ display: flex;
82
+ flex-wrap: wrap;
83
+ justify-content: center;
84
+ transition: opacity 0.5s ease-in-out;
85
+ transition-delay: 0.5s;
86
+ opacity: 0;
87
+ margin-top: 25px;
88
+ }
89
+
90
+ #sidebar-catergories > div {
91
+ background-color: var(--background-color);
92
+ border-radius: 1em;
93
+ margin: 0.5em 0.2em;
94
+ padding: 0.2em 0.5em;
95
+ -webkit-box-shadow: -3px 0px 5px -3px rgba(216, 216, 216, 0.75);
96
+ -moz-box-shadow: -3px 0px 5px -3px rgba(216, 216, 216, 0.75);
97
+ box-shadow: -3px 0px 5px -3px rgba(216, 216, 216, 0.75);
98
+ }
99
+
100
+ #sidebar-catergories > div > h6 {
101
+ color: var(--primary-font-color);
102
+ font-weight: normal;
103
+ font-size: 0.8em;
74
104
  }
75
105
 
76
106
  #sidebar-overlay-test-info {
@@ -101,21 +131,43 @@ module NxgCss
101
131
  height: fit-content;
102
132
  }
103
133
 
104
- #sidebar-title-wrap,
134
+ #sidebar-title-wrap {
135
+ display: grid;
136
+ grid-template-columns: 1fr auto;
137
+ place-items: center;
138
+ }
139
+
105
140
  #sidebar-overlay-test-info {
106
141
  display: flex;
107
142
  place-items: center;
108
143
  flex-wrap: wrap;
109
144
  }
145
+
146
+ #sidebar-title {
147
+ margin: 0 0.2em 0 0;
148
+ }
110
149
 
111
150
  #sidebar-title,
112
151
  #sidebar-status {
113
152
  color: var(--secondary-color);
153
+ transition: opacity 0.5s ease-in-out;
154
+ transition-delay: 0.5s;
155
+ opacity: 0;
114
156
  }
115
157
 
116
158
  #test-title {
117
159
  margin: 0;
118
160
  }
161
+
162
+ #test-execution-time {
163
+ font-size: 0.7em;
164
+ font-weight: bold;
165
+ background: var(--primary-gradient);
166
+ color: white;
167
+ padding: 0.2em 0.5em;
168
+ border-radius: 0.5em;
169
+ margin-left: 0.5em;
170
+ }
119
171
 
120
172
  h2,
121
173
  h3,
@@ -245,7 +297,7 @@ module NxgCss
245
297
  }
246
298
 
247
299
  #pt {
248
- font-size: 0.7em;
300
+ font-size: 0.8em;
249
301
  }
250
302
 
251
303
  .funcname {
@@ -0,0 +1,196 @@
1
+ require 'nxgcss.rb'
2
+ require 'nxgjs.rb'
3
+
4
+ module NxgHTML
5
+
6
+ include NxgCss
7
+ include NxgJavascript
8
+
9
+ def html(data_provider)
10
+ @data_provider = data_provider
11
+ "<html lang=\"en\">
12
+ #{head()}
13
+ #{body()}
14
+ #{js(@data_provider)}
15
+ </html>"
16
+ end
17
+
18
+ def head()
19
+ "<head>
20
+ <meta charset=\"UTF-8\" />
21
+ <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />
22
+ <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js\"></script>
23
+ <script> #{js_detect_system_dark_mode()}</script>
24
+ <title>Home | #{@data_provider[:title]}</title>
25
+ #{google_fonts_link()}
26
+ #{icons_link()}
27
+ #{css(@data_provider)}
28
+ </head>"
29
+ end
30
+
31
+ def google_fonts_link()
32
+ "<link
33
+ href=\"https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,600;0,700;0,800;1,300;1,400;1,600;1,700;1,800&display=swap\"
34
+ rel=\"stylesheet\"
35
+ />"
36
+ end
37
+
38
+ def icons_link()
39
+ "<link
40
+ href=\"https://fonts.googleapis.com/icon?family=Material+Icons\"
41
+ rel=\"stylesheet\"
42
+ />"
43
+ end
44
+
45
+ def body()
46
+ "<body id=\"app\" onload=\"onRefresh()\">
47
+ <div id=\"sidebar\" onclick=\"closeDetails(event)\">
48
+ <div id=\"sidebar-div\">
49
+ <div id=\"sidebar-title-wrap\">
50
+ <h1 id=\"sidebar-title\">Title</h1>
51
+ <i class=\"material-icons\" id=\"sidebar-status\">check_circle</i>
52
+ </div>
53
+ <div id=\"sidebar-catergories\">
54
+ </div>
55
+ </div>
56
+ </div>
57
+ <div id=\"sidebar-overlay\" onclick=\"closeDetails(event)\">
58
+ <div id=\"sidebar-overlay-grid\"></div>
59
+ </div>
60
+ <div id=\"body-wrap\">
61
+ #{header()}
62
+ #{config()}
63
+ #{features()}
64
+ #{footer()}
65
+ </div>
66
+ </body>"
67
+ end
68
+
69
+ def header()
70
+ "<div id=\"header\">
71
+ <h1 id=\"app-title\">#{@data_provider[:title]}</h1>
72
+ <div id=\"theme-wrap\">
73
+ <button id=\"theme-switch\" onclick=\"switchTheme()\">
74
+ <i class=\"material-icons\" id=\"theme-icon\">wb_sunny</i>
75
+ </button>
76
+ </div>
77
+ </div>"
78
+ end
79
+
80
+ def features()
81
+ "<div class=\"features-grid\"></div>"
82
+ end
83
+
84
+ def footer()
85
+ "<div id=\"footer\">
86
+ <p>
87
+ Developed by
88
+ <span>
89
+ <a
90
+ href=\"https://www.linkedin.com/in/balabharathijayaraman\"
91
+ rel=\"noopener\"
92
+ target=\"_blank\"
93
+ >Balabharathi Jayaraman</a
94
+ >
95
+ </span>
96
+ </p>
97
+ </div>"
98
+ end
99
+
100
+ def config()
101
+ return if @data_provider.length == 0
102
+
103
+ return "<div class=\"params-container\">
104
+ #{release_name()}
105
+ #{execution_date()}
106
+ #{device()}
107
+ #{os()}
108
+ #{app_version()}
109
+ #{environment()}
110
+ #{passed_tests()}
111
+ #{failed_tests()}
112
+ #{percentage_pass()}
113
+ #{execution_time()}
114
+ #{filter()}
115
+ </div>"
116
+ end
117
+
118
+ def execution_time()
119
+ return if !@data_provider.key?(:environment)
120
+
121
+ return config_item("Total execution time", @data_provider[:execution_time],'access_time')
122
+ end
123
+
124
+ def filter()
125
+ "<div class=\"param-wrap\" onclick=\"setFilter()\" id=\"filter\" title=\"Filter tests\">
126
+ <i class=\"pi material-icons\">filter_list</i>
127
+ <h5 id=\"pt\">Failed</h5>
128
+ </div>"
129
+ end
130
+
131
+ def passed_tests()
132
+ "<div class=\"param-wrap\" title=\"Passed tests\">
133
+ <i class=\"pi green-font material-icons\">check_circle</i>
134
+ <h5 id=\"pt\">#{@data_provider[:pass] == 0 ? "None" : @data_provider[:pass]}</h5>
135
+ </div>"
136
+ end
137
+
138
+ def failed_tests()
139
+ "<div class=\"param-wrap\" title=\"Failed tests\" #{@data_provider[:fail] > 0 ? "onclick=\"filterAllFailed()\" style=\"cursor: pointer\"" : ""}>
140
+ <i class=\"pi red-font material-icons\">cancel</i>
141
+ <h5 id=\"pt\">#{@data_provider[:fail] == 0 ? "None" : @data_provider[:fail]}</h5>
142
+ </div>"
143
+ end
144
+
145
+ def percentage_pass()
146
+ pass_percentage = ((@data_provider[:pass]/@data_provider[:total].to_f) * 100).round(2)
147
+
148
+ return "<div class=\"param-wrap\" title=\"Pass percentage\">
149
+ <i class=\"pi #{pass_percentage.to_i == 100 ? "green-font" : ""} material-icons\">equalizer</i>
150
+ <h5 id=\"pt\">#{pass_percentage.to_i == 100 ? pass_percentage.to_i : pass_percentage}%</h5>
151
+ </div>"
152
+ end
153
+
154
+ def environment()
155
+ return if !@data_provider.key?(:environment)
156
+
157
+ return config_item("Test environment", @data_provider[:environment], "layers")
158
+ end
159
+
160
+ def app_version()
161
+ return if !@data_provider.key?(:app_version)
162
+
163
+ return config_item("App version tested", @data_provider[:app_version], "info")
164
+ end
165
+
166
+ def release_name()
167
+ return if !@data_provider.key?(:release_name)
168
+
169
+ return config_item("Release", @data_provider[:release_name], "bookmark")
170
+ end
171
+
172
+ def os()
173
+ return if !@data_provider.key?(:os)
174
+
175
+ return config_item("Os tested", @data_provider[:os], "settings")
176
+ end
177
+
178
+ def device()
179
+ return if !@data_provider.key?(:device)
180
+
181
+ return config_item("Device tested", @data_provider[:device], "devices")
182
+ end
183
+
184
+ def execution_date()
185
+ @data_provider[:execution_date] = Time.now().strftime("%b %d, %Y") if !@data_provider.key?(:execution_date)
186
+
187
+ return config_item("Execution date", @data_provider[:execution_date], "event")
188
+ end
189
+
190
+ def config_item(toot_tip, name, icon)
191
+ "<div class=\"param-wrap\" title=\"#{toot_tip}\">
192
+ <i class=\"pi material-icons\">#{icon}</i>
193
+ <h5 id=\"pt\">#{name}</h5>
194
+ </div>"
195
+ end
196
+ end
@@ -0,0 +1,233 @@
1
+
2
+ module NxgJavascript
3
+
4
+ def js_detect_system_dark_mode()
5
+ "if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
6
+ $(document.documentElement).attr(\"theme\", \"dark\");
7
+ }"
8
+ end
9
+
10
+ def js(data_provider)
11
+
12
+ js_array = data_provider[:features].to_s.gsub("=>", ":")
13
+
14
+ return "<script>
15
+ const allFeatures = #{js_array};
16
+ const STATUS = { pass: \"check_circle\", fail: \"cancel\", };
17
+ var displayFailuresOnly = false;
18
+ var dataSource = [];
19
+ var catergories = [];
20
+
21
+ function onRefresh() {
22
+ dataSource = allFeatures;
23
+ setFilter();
24
+ currentTheme = $(document.documentElement).attr(\"theme\");
25
+ $(\"#theme-icon\").text(currentTheme === \"dark\" ? \"brightness_2\" : \"wb_sunny\");
26
+ }
27
+
28
+ function updateView() {
29
+ $(\".banner\").removeClass(\"banner\").addClass(\"features-grid\");
30
+ $(\".features-grid\").empty();
31
+
32
+ if (dataSource.length === 0) {
33
+ console.log(\"inside\");
34
+ $(\".features-grid\")
35
+ .removeClass(\"features-grid\")
36
+ .addClass(\"banner\")
37
+ .append(
38
+ `<i class=\"banner-text green-font material-icons\">done_all</i><h1>No Failures</>`
39
+ );
40
+ return;
41
+ }
42
+
43
+ dataSource.forEach((feature, index) => {
44
+ $(\".features-grid\").append(
45
+ `<div class=\"module dark ${
46
+ feature.fail > 0 ? \"red-bg\" : \"\"
47
+ }\" onclick=\"showDetails(${index})\"><div class=\"funcname\"><h4>${
48
+ feature.name
49
+ }</h4></div><div class=\"total\"><h6>Total</h6><h4>${
50
+ feature.total
51
+ }</h4></div><div class=\"pass green-font\"><h6>Passed</h6><h4>${
52
+ feature.pass
53
+ }</h4></div><div class=\"fail red-font\"><h6>Failed</h6><h4>${
54
+ feature.fail
55
+ }</h4></div></div>`
56
+ );
57
+ });
58
+ }
59
+
60
+ function setFilter() {
61
+ if (displayFailuresOnly) {
62
+ $(\"#filter h5\").text(\"Failed\");
63
+ dataSource = allFeatures.filter((feature) => {
64
+ return feature.fail > 0;
65
+ });
66
+ } else {
67
+ $(\"#filter h5\").text(\"All\");
68
+ dataSource = allFeatures;
69
+ }
70
+ updateView();
71
+ displayFailuresOnly = !displayFailuresOnly;
72
+ }
73
+
74
+ function filterAllFailed() {
75
+ allFailedTests = [];
76
+ catergories = [];
77
+
78
+ failedFeatures = allFeatures.filter((feature) => {
79
+ return feature.fail > 0;
80
+ });
81
+
82
+ for (index = 0; index < failedFeatures.length; index++) {
83
+ failedFeatures[index].tests.filter((test) => {
84
+ if (!test.testPass) {
85
+ allFailedTests.push(test);
86
+ }
87
+ });
88
+ }
89
+
90
+ $(\"#body-wrap\").css(\"overflow\", \"hidden\");
91
+ $(\"#sidebar-overlay\").css(\"overflow\", \"auto\");
92
+ $(\"#sidebar-overlay\").css(\"visibility\", \"visible\");
93
+ $(\"#sidebar-overlay\").css(\"margin-left\", \"40%\");
94
+ $(\"#sidebar\").css(\"width\", \"40%\");
95
+ $(\"#sidebar-title\").css(\"visibility\", \"visible\");
96
+ $(\"#sidebar-status\").css(\"visibility\", \"visible\");
97
+ $(\"#sidebar-title\").css(\"opacity\", \"1\");
98
+ $(\"#sidebar-status\").css(\"opacity\", \"1\");
99
+ $(\"#sidebar-catergories\").css(\"visibility\", \"visible\");
100
+ $(\"#sidebar-catergories\").css(\"opacity\", \"1\");
101
+ /* Update Test Information */
102
+
103
+ $(\"#sidebar-title\").text(\"Failed Tests\");
104
+ $(\"#sidebar-status\").text(STATUS.fail);
105
+ $(\"#sidebar-overlay-grid\").empty();
106
+ allFailedTests.forEach((test) => {
107
+ $(\"#sidebar-overlay-grid\").append(
108
+ `<div id=\"sidebar-overlay-test-info\" onclick=\"()=>{}\"><i class=\"${
109
+ test.testPass ? \"green-font\" : \"red-font\"
110
+ } material-icons\" style=\"font-size: 1em\">${
111
+ STATUS.fail
112
+ }</i>&nbsp;&nbsp;<h4 id=\"test-title\">${test.name}</h4><div><h4 id=\"test-execution-time\">${test.time} secs</h4></div>${
113
+ test.comments !== \"\"
114
+ ? `<p id=\"error-message\">${test.comments}</p>`
115
+ : \"\"
116
+ }</div>`
117
+ );
118
+ categorize(test);
119
+ });
120
+ displayCategories();
121
+ }
122
+
123
+ function displayCategories() {
124
+ $(\"#sidebar-catergories\").empty();
125
+ if(catergories.length === 1) { return; }
126
+ catergories.forEach((cat) => {$(\"#sidebar-catergories\").append(`<div><h6>#${cat.name}</h6></div>`);});
127
+ }
128
+
129
+ function switchTheme() {
130
+ currentTheme = $(document.documentElement).attr(\"theme\");
131
+ $(document.documentElement).attr(\"theme\", currentTheme === \"dark\" ? \"light\" : \"dark\");
132
+ $(\"#theme-icon\").text(currentTheme === \"dark\" ? \"wb_sunny\" : \"brightness_2\");
133
+ }
134
+
135
+ function closeDetails(e) {
136
+ if (e.target.id === \"sidebar\" || e.target.id === \"sidebar-overlay\") {
137
+ $(\"#sidebar-catergories\").css(\"visibility\", \"hidden\");
138
+ $(\"#sidebar-catergories\").css(\"opacity\", \"0\");
139
+ $(\"#sidebar-title\").css(\"visibility\", \"hidden\");
140
+ $(\"#sidebar-status\").css(\"visibility\", \"hidden\");
141
+ $(\"#sidebar-title\").css(\"opacity\", \"0\");
142
+ $(\"#sidebar-status\").css(\"opacity\", \"0\");
143
+ $(\"#sidebar-overlay\").css(\"margin-left\", \"0\");
144
+ $(\"#sidebar-overlay\").css(\"visibility\", \"hidden\");
145
+ $(\"#sidebar\").css(\"width\", \"0\");
146
+ $(\"#body-wrap\").css(\"overflow\", \"auto\");
147
+ $(\"#sidebar-overlay\").css(\"overflow\", \"hidden\");
148
+ }
149
+ }
150
+
151
+ window
152
+ .matchMedia(\"(prefers-color-scheme: dark)\")
153
+ .addEventListener(\"change\", (e) => {
154
+ darkTheme = e.matches;
155
+ $(document.documentElement).attr(\"theme\", darkTheme ? \"dark\" : \"light\");
156
+ $(\"#theme-icon\").text(darkTheme ? \"brightness_2\" : \"wb_sunny\");
157
+ });
158
+
159
+ function showDetails(featureID) {
160
+ feature = dataSource[featureID];
161
+ catergories = [];
162
+
163
+ $(\"#body-wrap\").css(\"overflow\", \"hidden\");
164
+ $(\"#sidebar-overlay\").css(\"overflow\", \"auto\");
165
+ $(\"#sidebar-overlay\").css(\"visibility\", \"visible\");
166
+ $(\"#sidebar-overlay\").css(\"margin-left\", \"40%\");
167
+ $(\"#sidebar\").css(\"width\", \"40%\");
168
+ $(\"#sidebar-title\").css(\"visibility\", \"visible\");
169
+ $(\"#sidebar-status\").css(\"visibility\", \"visible\");
170
+ $(\"#sidebar-title\").css(\"opacity\", \"1\");
171
+ $(\"#sidebar-status\").css(\"opacity\", \"1\");
172
+ $(\"#sidebar-catergories\").css(\"visibility\", \"visible\");
173
+ $(\"#sidebar-catergories\").css(\"opacity\", \"1\");
174
+ /* Update Test Information */
175
+
176
+ $(\"#sidebar-title\").text(feature.name);
177
+ $(\"#sidebar-overlay-grid\").empty();
178
+ feature.tests.forEach((test) => {
179
+ $(\"#sidebar-overlay-grid\").append(
180
+ `<div id=\"sidebar-overlay-test-info\" onclick=\"()=>{}\"><i class=\"${
181
+ test.testPass ? \"green-font\" : \"red-font\"
182
+ } material-icons\" style=\"font-size: 1em\">${
183
+ test.testPass ? STATUS.pass : STATUS.fail
184
+ }</i>&nbsp;&nbsp;<h4 id=\"test-title\">${test.name}</h4><div><h4 id=\"test-execution-time\">${test.time} secs</h4></div>${
185
+ test.comments !== \"\"
186
+ ? `<p id=\"error-message\">${test.comments}</p>`
187
+ : \"\"
188
+ }</div>`
189
+ );
190
+ categorize(test);
191
+ });
192
+ displayCategories();
193
+
194
+ for (index = 0; index < feature.tests.length; index++) {
195
+ if (!feature.tests[index].testPass) {
196
+ $(\"#sidebar-status\").text(STATUS.fail);
197
+ return;
198
+ }
199
+ }
200
+ $(\"#sidebar-status\").text(STATUS.pass);
201
+ }
202
+
203
+ function categorize(test) {
204
+ if (test.tag === \"\") {
205
+ return;
206
+ }
207
+ if (!catergoryAdded(test.tag)) {
208
+ catergories.push({ name: test.tag.toLowerCase(), tests: [test] });
209
+ } else {
210
+ catergories[catergoryIndex(test.tag)].tests.push(test);
211
+ }
212
+ }
213
+
214
+ function catergoryAdded(category) {
215
+ for (var i = 0; i < catergories.length; i++) {
216
+ if (catergories[i].name === category.toLowerCase()) {
217
+ return true;
218
+ }
219
+ }
220
+ return false;
221
+ }
222
+
223
+ function catergoryIndex(category) {
224
+ for (var i = 0; i < catergories.length; i++) {
225
+ if (catergories[i].name === category.toLowerCase()) {
226
+ return i;
227
+ }
228
+ }
229
+ return 0;
230
+ }
231
+ </script>"
232
+ end
233
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nxgreport
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Balabharathi Jayaraman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-25 00:00:00.000000000 Z
11
+ date: 2020-10-01 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Generate a beautiful static test report.
14
14
  email: balabharathi.jayaraman@gmail.com
@@ -18,6 +18,8 @@ extra_rdoc_files: []
18
18
  files:
19
19
  - lib/nxgcore.rb
20
20
  - lib/nxgcss.rb
21
+ - lib/nxghtml.rb
22
+ - lib/nxgjs.rb
21
23
  - lib/nxgreport.rb
22
24
  homepage: https://rubygemspec.org/gems/nxgreport
23
25
  licenses: