nxgreport 0.6.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.
@@ -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
@@ -1,365 +1,5 @@
1
1
 
2
- class NxgReport
2
+ require 'fileutils'
3
+ require 'nxgcore.rb'
3
4
 
4
- attr_reader :nxg_report_path, :auto_open, :title
5
-
6
- def setup(location: "./NxgReport.html", title: "Features Summary")
7
- @nxg_report_path = location
8
- @title = title
9
- @auto_open = false
10
- @features = Hash.new()
11
- end
12
-
13
- def open_upon_execution(value:true)
14
- @auto_open = value
15
- end
16
-
17
- def log_test(feature_name, test_status)
18
- test_pass = test_status.downcase.include?('pass')
19
- if @features.key? feature_name
20
- @features[feature_name][0]+=1
21
- @features[feature_name][(test_pass) ? 1 : 2]+=1
22
- else
23
- @features[feature_name]=[0,0,0]
24
- @features[feature_name][0]+=1
25
- @features[feature_name][(test_pass) ? 1 : 2]+=1
26
- end
27
- end
28
-
29
- def build()
30
- write()
31
- if @auto_open && report_success()
32
- system("open #{@nxg_report_path}")
33
- end
34
- end
35
-
36
- # Private methods
37
- def log(message)
38
- puts("🤖- #{message}")
39
- end
40
-
41
- def report_success()
42
- return File.file?(@nxg_report_path)
43
- end
44
-
45
- def clean()
46
- File.delete(@nxg_report_path) if File.file?(@nxg_report_path)
47
- end
48
-
49
- def htmlize(features)
50
- html_content = ''
51
- features.each do |name, metrics|
52
- html_content += "\n<div class=\"module dark #{metrics[2] != 0 ? 'danger' : ''} \">
53
- <div class=\"funcname\">
54
- <h4>#{name}</h4>
55
- </div>
56
- <div class=\"total\">
57
- <h6>Total</h6>
58
- <h4>#{metrics[0]}</h4>
59
- </div>
60
- <div class=\"pass\">
61
- <h6>Passed</h6>
62
- <h4>#{metrics[1]}</h4>
63
- </div>
64
- <div class=\"fail\">
65
- <h6>Failed</h6>
66
- <h4>#{metrics[2]}</h4>
67
- </div>
68
- </div>"
69
- end
70
- return html_content
71
- end
72
-
73
- def write()
74
- if @features.length == 0
75
- log("No tests logged, cannot build empty report.")
76
- return
77
- end
78
- clean()
79
- template = File.new(@nxg_report_path, 'w')
80
- template.puts("<html lang=\"en\">
81
- <head>
82
- <meta charset=\"UTF-8\" />
83
- <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />
84
- <title id=\"meta-app-title\"></title>
85
- <link
86
- 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\"
87
- rel=\"stylesheet\"
88
- />
89
- <link
90
- href=\"https://fonts.googleapis.com/icon?family=Material+Icons\"
91
- rel=\"stylesheet\"
92
- />
93
- <style>
94
- :root {
95
- --dark-bg: rgb(41, 40, 40);
96
- --dark-primary: #050505;
97
- --dark-font: rgb(201, 201, 201);
98
- --dark-blue: rgb(0, 225, 255);
99
- --dark-green: rgba(115, 255, 0, 0.89);
100
- --dark-red: rgb(255, 0, 0);
101
-
102
- --light-bg: rgb(226, 226, 226);
103
- --light-primary: #fff;
104
- --light-font: rgb(44, 44, 44);
105
- --light-blue: rgb(1, 67, 165);
106
- --light-green: rgb(14, 138, 2);
107
- --light-red: rgb(255, 0, 0);
108
-
109
- --font: \"Open Sans\", sans-serif;
110
- --danger-bg: rgba(255, 0, 0, 0.185);
111
- }
112
-
113
- body {
114
- font-family: var(--font);
115
- margin: auto;
116
- }
117
-
118
- .wrapper {
119
- display: grid;
120
- grid-template-rows: auto 1fr;
121
- height: 100vh;
122
- width: 100vw;
123
- }
124
-
125
- .header {
126
- display: grid;
127
- grid-template-columns: 6fr 1fr;
128
- text-align: center;
129
- background: linear-gradient(to bottom right, #ff644e, #cb3018);
130
- }
131
-
132
- .mc {
133
- display: grid;
134
- grid-template-columns: 1fr 1fr 1fr;
135
- grid-auto-rows: 70px;
136
- grid-gap: 0.5em;
137
- padding: 0.5em 2em;
138
- padding-top: 2em;
139
- }
140
-
141
- .footer {
142
- margin-bottom: 1em;
143
- padding: 3em;
144
- text-align: center;
145
- font-size: 0.7rem;
146
- font-weight: 600;
147
- }
148
-
149
- a {
150
- cursor: pointer;
151
- font-weight: 600;
152
- }
153
-
154
- .module {
155
- display: grid;
156
- place-items: center;
157
- grid-template-columns: 3fr 1fr 1fr 1fr;
158
- border-radius: 0.7rem;
159
- padding: 10px 10px;
160
- }
161
-
162
- .button-wrapper {
163
- place-items: center;
164
- }
165
-
166
- #theme-switch {
167
- width: 5em;
168
- height: 5em;
169
- background-color: Transparent;
170
- background-repeat: no-repeat;
171
- border: none;
172
- cursor: pointer;
173
- overflow: hidden;
174
- outline: none;
175
- margin: 0;
176
- position: relative;
177
- top: 50%;
178
- -ms-transform: translateY(-50%);
179
- transform: translateY(-50%);
180
- }
181
-
182
- h2,
183
- h3,
184
- h4,
185
- h5,
186
- h6 {
187
- text-align: center;
188
- margin: auto;
189
- }
190
-
191
- .total,
192
- .pass,
193
- .fail {
194
- display: grid;
195
- width: 100%;
196
- height: 100%;
197
- place-items: center;
198
- }
199
-
200
- body.dark {
201
- background-color: var(--dark-bg);
202
- color: var(--dark-font);
203
- }
204
-
205
- body.dark > .wrapper > .footer {
206
- color: var(--dark-font);
207
- }
208
-
209
- body.dark > .wrapper > .mc > .module {
210
- background-color: var(--dark-primary);
211
- color: var(--dark-font);
212
- }
213
-
214
- body.dark > .wrapper > .mc > .module > .total {
215
- color: var(--dark-blue);
216
- }
217
-
218
- body.dark > .wrapper > .mc > .module > .pass {
219
- color: var(--dark-green);
220
- }
221
-
222
- body.dark > .wrapper > .mc > .module > .fail {
223
- color: var(--dark-red);
224
- }
225
-
226
- body.dark > .wrapper > .mc > .module.danger {
227
- background-color: rgba(255, 0, 0, 0.185);
228
- }
229
-
230
- body.dark > .wrapper > .header {
231
- color: var(--dark-primary);
232
- }
233
-
234
- body.dark > .wrapper > .footer > p > span > a {
235
- color: var(--dark-font);
236
- }
237
-
238
- body.dark > .wrapper > .header > div > button > #theme-switch-icon {
239
- color: var(--dark-primary);
240
- }
241
-
242
- body {
243
- background-color: var(--light-bg);
244
- color: var(--dark-font);
245
- }
246
-
247
- body > .wrapper > .footer {
248
- color: var(--light-font);
249
- }
250
-
251
- body > .wrapper > .mc > .module {
252
- background-color: var(--light-primary);
253
- color: var(--light-font);
254
- }
255
-
256
- body > .wrapper > .mc > .module > .total {
257
- color: var(--light-blue);
258
- }
259
-
260
- body > .wrapper > .mc > .module > .pass {
261
- color: var(--light-green);
262
- }
263
-
264
- body > .wrapper > .mc > .module > .fail {
265
- color: var(--light-red);
266
- }
267
-
268
- body > .wrapper > .mc > .module.danger {
269
- background-color: var(--danger-bg);
270
- }
271
-
272
- body > .wrapper > .header {
273
- color: var(--light-primary);
274
- }
275
-
276
- body > .wrapper > .footer > p > span > a {
277
- color: var(--light-font);
278
- }
279
-
280
- body > .wrapper > .header > div > button > #theme-switch-icon {
281
- color: var(--light-primary);
282
- }
283
-
284
- @media only screen and (max-width: 600px) {
285
- h1 {
286
- font-size: 24px;
287
- }
288
-
289
- .mc {
290
- grid-template-columns: 1fr;
291
- padding: 0.5em 0.5em;
292
- padding-top: 1em;
293
- }
294
- }
295
-
296
- @media (min-width: 600px) and (max-width: 992px) {
297
- .mc {
298
- grid-template-columns: 1fr 1fr;
299
- }
300
- }
301
- </style>
302
- </head>
303
- <body class=\"dark\" id=\"app\">
304
- <div class=\"wrapper\">
305
- <div class=\"header\">
306
- <h1 id=\"app-title\"></h1>
307
- <div class=\"button-wrapper\">
308
- <button id=\"theme-switch\" onclick=\"handleThemeSwitch()\">
309
- <i class=\"material-icons\" id=\"theme-switch-icon\">brightness_2</i>
310
- </button>
311
- </div>
312
- </div>
313
- <div class=\"mc\">
314
- #{htmlize(@features)}
315
- </div>
316
- <div class=\"footer\">
317
- <p>
318
- Developed by
319
- <span
320
- ><a
321
- href=\"https://www.linkedin.com/in/balabharathijayaraman\"
322
- rel=\"nofollow\"
323
- target=\"_blank\"
324
- >Balabharathi Jayaraman</a
325
- ></span
326
- >
327
- </p>
328
- </div>
329
- </div>
330
- </body>
331
- <script>
332
- var appTitle = \"#{@title}\";
333
- var theme = \"dark\";
334
-
335
- window.onload = function () {
336
- document.getElementById(
337
- \"meta-app-title\"
338
- ).innerHTML = `Home | ${appTitle}`;
339
- document.getElementById(\"app-title\").innerHTML = appTitle;
340
- };
341
-
342
- function handleThemeSwitch() {
343
- if (theme === \"dark\") {
344
- theme = \"light\";
345
- document.getElementById(\"app\").classList.remove(\"dark\");
346
- document.getElementById(\"theme-switch-icon\").innerHTML = \"wb_sunny\";
347
- document.getElementById(\"theme-switch-icon\");
348
- return;
349
- }
350
- if (theme === \"light\") {
351
- theme = \"dark\";
352
- document.getElementById(\"app\").classList.add(\"dark\");
353
- document.getElementById(\"theme-switch-icon\").innerHTML = \"brightness_2\";
354
- }
355
- }
356
- </script>
357
- </html>
358
- ")
359
- template.close()
360
- end
361
-
362
- private :log, :clean, :write, :htmlize, :report_success
363
- end
364
-
365
- $NxgReport = NxgReport.new()
5
+ $NxgReport = NxgCore.new().instance()