nxgreport 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: