monocart-reporter 2.9.6 → 2.9.7

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.
package/lib/utils/util.js CHANGED
@@ -1,511 +1,511 @@
1
- const fs = require('fs');
2
- const { writeFile, readFile } = require('fs/promises');
3
- const path = require('path');
4
- const os = require('os');
5
- const crypto = require('crypto');
6
- const EC = require('eight-colors');
7
- const CG = require('console-grid');
8
- const Share = require('../platform/share.js');
9
-
10
- const getDefaultOptions = require('../default/options.js');
11
-
12
- const Util = {
13
- ... Share,
14
-
15
- EC,
16
- CG,
17
-
18
- root: process.cwd(),
19
-
20
- relativePath: function(p, root) {
21
- p = `${p}`;
22
- root = `${root || Util.root}`;
23
- let rp = path.relative(root, p);
24
- rp = Util.formatPath(rp);
25
- return rp;
26
- },
27
-
28
- replace: function(str, obj, defaultValue) {
29
- str = `${str}`;
30
- if (!obj) {
31
- return str;
32
- }
33
- str = str.replace(/\{([^}{]+)\}/g, function(match, key) {
34
- if (!Util.hasOwn(obj, key)) {
35
- if (typeof (defaultValue) !== 'undefined') {
36
- return defaultValue;
37
- }
38
- return match;
39
- }
40
- let val = obj[key];
41
- if (typeof (val) === 'function') {
42
- val = val(obj, key);
43
- }
44
- if (typeof (val) === 'undefined') {
45
- val = '';
46
- }
47
- return val;
48
- });
49
- return str;
50
- },
51
-
52
- calculateSha1(buffer) {
53
- const hash = crypto.createHash('sha1');
54
- hash.update(buffer);
55
- return hash.digest('hex');
56
- },
57
-
58
- calculateId: (id) => {
59
- if (id) {
60
- return Util.calculateSha1(id).slice(0, 20);
61
- }
62
- return Util.uid();
63
- },
64
-
65
- parseComments: (input) => {
66
- const str = `${input}`;
67
- // starts with @ , ends without @ encodeURIComponent("@") %40
68
- const reg = /@(\w+)\s+([^@]+)/g;
69
- const matches = str.matchAll(reg);
70
- const parsed = {};
71
- for (const match of matches) {
72
- // 0 is whole matched, and remove ends * or */
73
- parsed[match[1]] = match[2].trim().replace(/(\*+|\*+\/)$/g, '').trim();
74
- }
75
- // console.log(parsed);
76
- return parsed;
77
- },
78
-
79
- resolveOutputDir: (testInfo) => {
80
- const reporterOptions = Util.resolveReporterOptions(testInfo);
81
- const outputFile = Util.resolveOutputFile(reporterOptions.outputFile);
82
- const outputDir = path.dirname(outputFile);
83
- return outputDir;
84
- },
85
-
86
- resolveOutputFile: (outputFile) => {
87
-
88
- // then check string
89
- if (!outputFile || typeof outputFile !== 'string') {
90
- outputFile = getDefaultOptions().outputFile;
91
- }
92
-
93
- // end with html
94
- if (!outputFile.endsWith('.html')) {
95
- outputFile = path.join(outputFile, 'index.html');
96
- }
97
-
98
- return path.resolve(outputFile);
99
- },
100
-
101
- resolveLogging: (testInfo, options) => {
102
- if (options && options.logging) {
103
- return options.logging;
104
- }
105
- const reporterOptions = Util.resolveReporterOptions(testInfo);
106
- return reporterOptions.logging;
107
- },
108
-
109
- // eslint-disable-next-line complexity
110
- resolveReporterOptions: (testInfo) => {
111
- if (Util.reporterOptions) {
112
- return Util.reporterOptions;
113
- }
114
- if (!testInfo) {
115
- return {};
116
- }
117
- const configReporters = testInfo.config.reporter;
118
- if (Array.isArray(configReporters)) {
119
- for (const item of configReporters) {
120
- if (Array.isArray(item)) {
121
- const [name, options] = item;
122
- if (name && name.indexOf('monocart-reporter') !== -1) {
123
- Util.reporterOptions = options;
124
- return options || {};
125
- }
126
- }
127
- }
128
- }
129
- return {};
130
- },
131
-
132
- resolveTestIdWithRetry: (testInfo) => {
133
- const id = Util.calculateId(testInfo.testId);
134
- const retry = testInfo.retry;
135
- if (retry > 0) {
136
- return `${id}-retry${retry}`;
137
- }
138
- return id;
139
- },
140
-
141
- resolveArtifactSourcePath: (artifactsDir, id) => {
142
- const filename = `source-${id}.json`;
143
- const sourcePath = path.resolve(artifactsDir, filename);
144
- return sourcePath;
145
- },
146
-
147
- // empty or create dir
148
- initDir: (dirPath) => {
149
- if (fs.existsSync(dirPath)) {
150
- Util.rmSync(dirPath);
151
- }
152
- fs.mkdirSync(dirPath, {
153
- recursive: true
154
- });
155
- },
156
-
157
- getEOL: function(content) {
158
- if (!content) {
159
- return os.EOL;
160
- }
161
- const nIndex = content.lastIndexOf('\n');
162
- if (nIndex === -1) {
163
- return os.EOL;
164
- }
165
- if (content.substr(nIndex - 1, 1) === '\r') {
166
- return '\r\n';
167
- }
168
- return '\n';
169
- },
170
-
171
- getAttachmentPathExtras: function(d) {
172
- return {
173
- name: d.name,
174
- cwd: d.cwd,
175
- outputDir: d.outputDir
176
- };
177
- },
178
-
179
- readJSONSync: function(filePath) {
180
- // do NOT use require, it has cache
181
- const content = Util.readFileSync(filePath);
182
- if (content) {
183
- return JSON.parse(content);
184
- }
185
- },
186
-
187
- writeJSONSync: function(filePath, json) {
188
- let content = Util.jsonString(json);
189
- if (!content) {
190
- Util.logError('invalid JSON object');
191
- return false;
192
- }
193
- // end of line
194
- const EOL = Util.getEOL();
195
- content = content.replace(/\r|\n/g, EOL);
196
- content += EOL;
197
- Util.writeFileSync(filePath, content);
198
- return true;
199
- },
200
-
201
- jsonString: function(obj, spaces) {
202
-
203
- if (typeof obj === 'string') {
204
- return obj;
205
- }
206
-
207
- if (!spaces) {
208
- spaces = 4;
209
- }
210
-
211
- let str = '';
212
- try {
213
- str = JSON.stringify(obj, null, spaces);
214
- } catch (e) {
215
- console.log(e);
216
- }
217
-
218
- return str;
219
- },
220
-
221
- readFileSync: function(filePath) {
222
- if (fs.existsSync(filePath)) {
223
- // Returns: <string> | <Buffer>
224
- const buf = fs.readFileSync(filePath);
225
- if (Buffer.isBuffer(buf)) {
226
- return buf.toString('utf8');
227
- }
228
- return buf;
229
- }
230
- },
231
-
232
- readFile: async (filePath) => {
233
- if (fs.existsSync(filePath)) {
234
- const buf = await readFile(filePath).catch((e) => {
235
- Util.logError(`read file: ${filePath} ${e.message || e}`);
236
- });
237
- if (Buffer.isBuffer(buf)) {
238
- return buf.toString('utf8');
239
- }
240
- return buf;
241
- }
242
- },
243
-
244
- writeFileSync: function(filePath, content) {
245
- if (!fs.existsSync(filePath)) {
246
- const p = path.dirname(filePath);
247
- if (!fs.existsSync(p)) {
248
- fs.mkdirSync(p, {
249
- recursive: true
250
- });
251
- }
252
- }
253
- fs.writeFileSync(filePath, content);
254
- },
255
-
256
- writeFile: async (filePath, content) => {
257
- if (!fs.existsSync(filePath)) {
258
- const p = path.dirname(filePath);
259
- if (!fs.existsSync(p)) {
260
- fs.mkdirSync(p, {
261
- recursive: true
262
- });
263
- }
264
- }
265
- await writeFile(filePath, content).catch((e) => {
266
- Util.logError(`write file: ${filePath} ${e.message || e}`);
267
- });
268
- },
269
-
270
- rmSync: (p) => {
271
- if (fs.existsSync(p)) {
272
- fs.rmSync(p, {
273
- recursive: true,
274
- force: true,
275
- maxRetries: 10
276
- });
277
- }
278
- },
279
-
280
- // eslint-disable-next-line complexity
281
- forEachFile: (dir, callback, shallow) => {
282
- if (!fs.existsSync(dir)) {
283
- return;
284
- }
285
-
286
- const isBreak = (res) => {
287
- return res === 'break' || res === false;
288
- };
289
-
290
- const dirs = [];
291
- const list = fs.readdirSync(dir, {
292
- withFileTypes: true
293
- });
294
-
295
- for (const item of list) {
296
-
297
- if (item.isFile()) {
298
- const res = callback(item.name, dir);
299
- if (isBreak(res)) {
300
- return res;
301
- }
302
- continue;
303
- }
304
-
305
- if (item.isDirectory()) {
306
- dirs.push(path.resolve(dir, item.name));
307
- }
308
-
309
- }
310
-
311
- if (shallow) {
312
- return;
313
- }
314
-
315
- for (const subDir of dirs) {
316
- const res = Util.forEachFile(subDir, callback, shallow);
317
- if (isBreak(res)) {
318
- return res;
319
- }
320
- }
321
-
322
- },
323
-
324
- getTemplate: function(templatePath) {
325
- if (!Util.templateCache) {
326
- Util.templateCache = {};
327
- }
328
- let template = Util.templateCache[templatePath];
329
- if (!template) {
330
- template = Util.readFileSync(templatePath);
331
- if (template) {
332
- Util.templateCache[templatePath] = template;
333
- } else {
334
- Util.logError(`not found template: ${templatePath}`);
335
- }
336
- }
337
- return template;
338
- },
339
-
340
- getDuration: (dateRanges, durationStrategy) => {
341
- dateRanges.sort((a, b) => {
342
- if (a.start === b.start) {
343
- return a.end - b.end;
344
- }
345
- return a.start - b.start;
346
- });
347
-
348
- if (durationStrategy === 'exclude-idle') {
349
-
350
- dateRanges.reduce((prevRange, range) => {
351
- // same start
352
- if (range.start === prevRange.start) {
353
- range.dedupe = true;
354
- // equal prev
355
- if (range.end === prevRange.end) {
356
- return prevRange;
357
- }
358
- // great than the prev end, update the end
359
- prevRange.end = range.end;
360
- return prevRange;
361
- }
362
-
363
- // already in the range
364
- if (range.end <= prevRange.end) {
365
- range.dedupe = true;
366
- return prevRange;
367
- }
368
-
369
- // collected, update the end
370
- if (range.start <= prevRange.end) {
371
- range.dedupe = true;
372
- prevRange.end = range.end;
373
- return prevRange;
374
- }
375
-
376
- return range;
377
-
378
- });
379
-
380
- const ranges = dateRanges.filter((it) => !it.dedupe);
381
- // console.log(ranges);
382
- let duration = 0;
383
- ranges.forEach((item) => {
384
- duration += item.end - item.start;
385
- });
386
-
387
- return duration;
388
- }
389
- // normal
390
- const dateStart = dateRanges[0].start;
391
- const endDate = dateRanges[dateRanges.length - 1].end;
392
- const duration = endDate - dateStart;
393
- return duration;
394
- },
395
-
396
- mergeOption: function(... args) {
397
- const option = {};
398
- args.forEach((item) => {
399
- if (!item) {
400
- return;
401
- }
402
- Object.keys(item).forEach((k) => {
403
- const nv = item[k];
404
- if (Util.hasOwn(option, k)) {
405
- const ov = option[k];
406
- if (ov && typeof ov === 'object') {
407
- if (nv && typeof nv === 'object' && !Array.isArray(nv)) {
408
- option[k] = Util.mergeOption(ov, nv);
409
- return;
410
- }
411
- }
412
- }
413
- option[k] = nv;
414
- });
415
- });
416
- return option;
417
- },
418
-
419
- cmpVersion: (v1, v2) => {
420
- const [strMajor1, strMinor1, strPatch1] = `${v1}`.split('.');
421
- const [strMajor2, strMinor2, strPatch2] = `${v2}`.split('.');
422
- const strList = [strMajor1, strMinor1, strPatch1, strMajor2, strMinor2, strPatch2];
423
- const list = strList.map((str) => Util.toNum(parseInt(str)));
424
- const [major1, minor1, patch1, major2, minor2, patch2] = list;
425
- if (major1 === major2) {
426
- if (minor1 === minor2) {
427
- return patch1 - patch2;
428
- }
429
- return minor1 - minor2;
430
- }
431
- return major1 - major2;
432
- },
433
-
434
- // ==========================================================================================
435
-
436
- loggingLevels: {
437
- off: 0,
438
- error: 10,
439
- info: 20,
440
- debug: 30
441
- },
442
-
443
- initLoggingLevel: (level, from = '') => {
444
- if (!level && Util.loggingType) {
445
- return;
446
- }
447
- const types = {
448
- off: 'off',
449
- error: 'error',
450
- info: 'info',
451
- debug: 'debug'
452
- };
453
- const type = types[level] || types.info;
454
- Util.loggingType = type;
455
- Util.loggingLevel = Util.loggingLevels[type];
456
-
457
- // console.log('=========================================');
458
- // console.log(from, Util.loggingType, Util.loggingLevel);
459
- },
460
-
461
- logError: (message) => {
462
- if (Util.loggingLevel < Util.loggingLevels.error) {
463
- return;
464
- }
465
- EC.logRed(`[MR] ${message}`);
466
- },
467
-
468
- logInfo: (message) => {
469
- if (Util.loggingLevel < Util.loggingLevels.info) {
470
- return;
471
- }
472
- console.log(`[MR] ${message}`);
473
- },
474
-
475
- // grid is info level
476
- logGrid: (gridData) => {
477
- if (Util.loggingLevel < Util.loggingLevels.info) {
478
- return;
479
- }
480
- CG(gridData);
481
- },
482
-
483
- logDebug: (message) => {
484
- if (Util.loggingLevel < Util.loggingLevels.debug) {
485
- return;
486
- }
487
- console.log(`[MR] ${message}`);
488
- },
489
-
490
- // time is debug level
491
- logTime: (message, time_start) => {
492
- if (Util.loggingLevel < Util.loggingLevels.debug) {
493
- return;
494
- }
495
- const duration = Date.now() - time_start;
496
- const durationH = Util.TSF(duration);
497
- const ls = [`[MR] ${message}`, ' ('];
498
- if (duration <= 100) {
499
- ls.push(EC.green(durationH));
500
- } else if (duration < 500) {
501
- ls.push(EC.yellow(durationH));
502
- } else {
503
- ls.push(EC.red(durationH));
504
- }
505
- ls.push(')');
506
- console.log(ls.join(''));
507
- }
508
-
509
- };
510
-
511
- module.exports = Util;
1
+ const fs = require('fs');
2
+ const { writeFile, readFile } = require('fs/promises');
3
+ const path = require('path');
4
+ const os = require('os');
5
+ const crypto = require('crypto');
6
+ const EC = require('eight-colors');
7
+ const CG = require('console-grid');
8
+ const Share = require('../platform/share.js');
9
+
10
+ const getDefaultOptions = require('../default/options.js');
11
+
12
+ const Util = {
13
+ ... Share,
14
+
15
+ EC,
16
+ CG,
17
+
18
+ root: process.cwd(),
19
+
20
+ relativePath: function(p, root) {
21
+ p = `${p}`;
22
+ root = `${root || Util.root}`;
23
+ let rp = path.relative(root, p);
24
+ rp = Util.formatPath(rp);
25
+ return rp;
26
+ },
27
+
28
+ replace: function(str, obj, defaultValue) {
29
+ str = `${str}`;
30
+ if (!obj) {
31
+ return str;
32
+ }
33
+ str = str.replace(/\{([^}{]+)\}/g, function(match, key) {
34
+ if (!Util.hasOwn(obj, key)) {
35
+ if (typeof (defaultValue) !== 'undefined') {
36
+ return defaultValue;
37
+ }
38
+ return match;
39
+ }
40
+ let val = obj[key];
41
+ if (typeof (val) === 'function') {
42
+ val = val(obj, key);
43
+ }
44
+ if (typeof (val) === 'undefined') {
45
+ val = '';
46
+ }
47
+ return val;
48
+ });
49
+ return str;
50
+ },
51
+
52
+ calculateSha1(buffer) {
53
+ const hash = crypto.createHash('sha1');
54
+ hash.update(buffer);
55
+ return hash.digest('hex');
56
+ },
57
+
58
+ calculateId: (id) => {
59
+ if (id) {
60
+ return Util.calculateSha1(id).slice(0, 20);
61
+ }
62
+ return Util.uid();
63
+ },
64
+
65
+ parseComments: (input) => {
66
+ const str = `${input}`;
67
+ // starts with @ , ends without @ encodeURIComponent("@") %40
68
+ const reg = /@(\w+)\s+([^@]+)/g;
69
+ const matches = str.matchAll(reg);
70
+ const parsed = {};
71
+ for (const match of matches) {
72
+ // 0 is whole matched, and remove ends * or */
73
+ parsed[match[1]] = match[2].trim().replace(/(\*+|\*+\/)$/g, '').trim();
74
+ }
75
+ // console.log(parsed);
76
+ return parsed;
77
+ },
78
+
79
+ resolveOutputDir: (testInfo) => {
80
+ const reporterOptions = Util.resolveReporterOptions(testInfo);
81
+ const outputFile = Util.resolveOutputFile(reporterOptions.outputFile);
82
+ const outputDir = path.dirname(outputFile);
83
+ return outputDir;
84
+ },
85
+
86
+ resolveOutputFile: (outputFile) => {
87
+
88
+ // then check string
89
+ if (!outputFile || typeof outputFile !== 'string') {
90
+ outputFile = getDefaultOptions().outputFile;
91
+ }
92
+
93
+ // end with html
94
+ if (!outputFile.endsWith('.html')) {
95
+ outputFile = path.join(outputFile, 'index.html');
96
+ }
97
+
98
+ return path.resolve(outputFile);
99
+ },
100
+
101
+ resolveLogging: (testInfo, options) => {
102
+ if (options && options.logging) {
103
+ return options.logging;
104
+ }
105
+ const reporterOptions = Util.resolveReporterOptions(testInfo);
106
+ return reporterOptions.logging;
107
+ },
108
+
109
+ // eslint-disable-next-line complexity
110
+ resolveReporterOptions: (testInfo) => {
111
+ if (Util.reporterOptions) {
112
+ return Util.reporterOptions;
113
+ }
114
+ if (!testInfo) {
115
+ return {};
116
+ }
117
+ const configReporters = testInfo.config.reporter;
118
+ if (Array.isArray(configReporters)) {
119
+ for (const item of configReporters) {
120
+ if (Array.isArray(item)) {
121
+ const [name, options] = item;
122
+ if (name && name.indexOf('monocart-reporter') !== -1) {
123
+ Util.reporterOptions = options;
124
+ return options || {};
125
+ }
126
+ }
127
+ }
128
+ }
129
+ return {};
130
+ },
131
+
132
+ resolveTestIdWithRetry: (testInfo) => {
133
+ const id = Util.calculateId(testInfo.testId);
134
+ const retry = testInfo.retry;
135
+ if (retry > 0) {
136
+ return `${id}-retry${retry}`;
137
+ }
138
+ return id;
139
+ },
140
+
141
+ resolveArtifactSourcePath: (artifactsDir, id) => {
142
+ const filename = `source-${id}.json`;
143
+ const sourcePath = path.resolve(artifactsDir, filename);
144
+ return sourcePath;
145
+ },
146
+
147
+ // empty or create dir
148
+ initDir: (dirPath) => {
149
+ if (fs.existsSync(dirPath)) {
150
+ Util.rmSync(dirPath);
151
+ }
152
+ fs.mkdirSync(dirPath, {
153
+ recursive: true
154
+ });
155
+ },
156
+
157
+ getEOL: function(content) {
158
+ if (!content) {
159
+ return os.EOL;
160
+ }
161
+ const nIndex = content.lastIndexOf('\n');
162
+ if (nIndex === -1) {
163
+ return os.EOL;
164
+ }
165
+ if (content.substr(nIndex - 1, 1) === '\r') {
166
+ return '\r\n';
167
+ }
168
+ return '\n';
169
+ },
170
+
171
+ getAttachmentPathExtras: function(d) {
172
+ return {
173
+ name: d.name,
174
+ cwd: d.cwd,
175
+ outputDir: d.outputDir
176
+ };
177
+ },
178
+
179
+ readJSONSync: function(filePath) {
180
+ // do NOT use require, it has cache
181
+ const content = Util.readFileSync(filePath);
182
+ if (content) {
183
+ return JSON.parse(content);
184
+ }
185
+ },
186
+
187
+ writeJSONSync: function(filePath, json) {
188
+ let content = Util.jsonString(json);
189
+ if (!content) {
190
+ Util.logError('invalid JSON object');
191
+ return false;
192
+ }
193
+ // end of line
194
+ const EOL = Util.getEOL();
195
+ content = content.replace(/\r|\n/g, EOL);
196
+ content += EOL;
197
+ Util.writeFileSync(filePath, content);
198
+ return true;
199
+ },
200
+
201
+ jsonString: function(obj, spaces) {
202
+
203
+ if (typeof obj === 'string') {
204
+ return obj;
205
+ }
206
+
207
+ if (!spaces) {
208
+ spaces = 4;
209
+ }
210
+
211
+ let str = '';
212
+ try {
213
+ str = JSON.stringify(obj, null, spaces);
214
+ } catch (e) {
215
+ console.log(e);
216
+ }
217
+
218
+ return str;
219
+ },
220
+
221
+ readFileSync: function(filePath) {
222
+ if (fs.existsSync(filePath)) {
223
+ // Returns: <string> | <Buffer>
224
+ const buf = fs.readFileSync(filePath);
225
+ if (Buffer.isBuffer(buf)) {
226
+ return buf.toString('utf8');
227
+ }
228
+ return buf;
229
+ }
230
+ },
231
+
232
+ readFile: async (filePath) => {
233
+ if (fs.existsSync(filePath)) {
234
+ const buf = await readFile(filePath).catch((e) => {
235
+ Util.logError(`read file: ${filePath} ${e.message || e}`);
236
+ });
237
+ if (Buffer.isBuffer(buf)) {
238
+ return buf.toString('utf8');
239
+ }
240
+ return buf;
241
+ }
242
+ },
243
+
244
+ writeFileSync: function(filePath, content) {
245
+ if (!fs.existsSync(filePath)) {
246
+ const p = path.dirname(filePath);
247
+ if (!fs.existsSync(p)) {
248
+ fs.mkdirSync(p, {
249
+ recursive: true
250
+ });
251
+ }
252
+ }
253
+ fs.writeFileSync(filePath, content);
254
+ },
255
+
256
+ writeFile: async (filePath, content) => {
257
+ if (!fs.existsSync(filePath)) {
258
+ const p = path.dirname(filePath);
259
+ if (!fs.existsSync(p)) {
260
+ fs.mkdirSync(p, {
261
+ recursive: true
262
+ });
263
+ }
264
+ }
265
+ await writeFile(filePath, content).catch((e) => {
266
+ Util.logError(`write file: ${filePath} ${e.message || e}`);
267
+ });
268
+ },
269
+
270
+ rmSync: (p) => {
271
+ if (fs.existsSync(p)) {
272
+ fs.rmSync(p, {
273
+ recursive: true,
274
+ force: true,
275
+ maxRetries: 10
276
+ });
277
+ }
278
+ },
279
+
280
+ // eslint-disable-next-line complexity
281
+ forEachFile: (dir, callback, shallow) => {
282
+ if (!fs.existsSync(dir)) {
283
+ return;
284
+ }
285
+
286
+ const isBreak = (res) => {
287
+ return res === 'break' || res === false;
288
+ };
289
+
290
+ const dirs = [];
291
+ const list = fs.readdirSync(dir, {
292
+ withFileTypes: true
293
+ });
294
+
295
+ for (const item of list) {
296
+
297
+ if (item.isFile()) {
298
+ const res = callback(item.name, dir);
299
+ if (isBreak(res)) {
300
+ return res;
301
+ }
302
+ continue;
303
+ }
304
+
305
+ if (item.isDirectory()) {
306
+ dirs.push(path.resolve(dir, item.name));
307
+ }
308
+
309
+ }
310
+
311
+ if (shallow) {
312
+ return;
313
+ }
314
+
315
+ for (const subDir of dirs) {
316
+ const res = Util.forEachFile(subDir, callback, shallow);
317
+ if (isBreak(res)) {
318
+ return res;
319
+ }
320
+ }
321
+
322
+ },
323
+
324
+ getTemplate: function(templatePath) {
325
+ if (!Util.templateCache) {
326
+ Util.templateCache = {};
327
+ }
328
+ let template = Util.templateCache[templatePath];
329
+ if (!template) {
330
+ template = Util.readFileSync(templatePath);
331
+ if (template) {
332
+ Util.templateCache[templatePath] = template;
333
+ } else {
334
+ Util.logError(`not found template: ${templatePath}`);
335
+ }
336
+ }
337
+ return template;
338
+ },
339
+
340
+ getDuration: (dateRanges, durationStrategy) => {
341
+ dateRanges.sort((a, b) => {
342
+ if (a.start === b.start) {
343
+ return a.end - b.end;
344
+ }
345
+ return a.start - b.start;
346
+ });
347
+
348
+ if (durationStrategy === 'exclude-idle') {
349
+
350
+ dateRanges.reduce((prevRange, range) => {
351
+ // same start
352
+ if (range.start === prevRange.start) {
353
+ range.dedupe = true;
354
+ // equal prev
355
+ if (range.end === prevRange.end) {
356
+ return prevRange;
357
+ }
358
+ // great than the prev end, update the end
359
+ prevRange.end = range.end;
360
+ return prevRange;
361
+ }
362
+
363
+ // already in the range
364
+ if (range.end <= prevRange.end) {
365
+ range.dedupe = true;
366
+ return prevRange;
367
+ }
368
+
369
+ // collected, update the end
370
+ if (range.start <= prevRange.end) {
371
+ range.dedupe = true;
372
+ prevRange.end = range.end;
373
+ return prevRange;
374
+ }
375
+
376
+ return range;
377
+
378
+ });
379
+
380
+ const ranges = dateRanges.filter((it) => !it.dedupe);
381
+ // console.log(ranges);
382
+ let duration = 0;
383
+ ranges.forEach((item) => {
384
+ duration += item.end - item.start;
385
+ });
386
+
387
+ return duration;
388
+ }
389
+ // normal
390
+ const dateStart = dateRanges[0].start;
391
+ const endDate = dateRanges[dateRanges.length - 1].end;
392
+ const duration = endDate - dateStart;
393
+ return duration;
394
+ },
395
+
396
+ mergeOption: function(... args) {
397
+ const option = {};
398
+ args.forEach((item) => {
399
+ if (!item) {
400
+ return;
401
+ }
402
+ Object.keys(item).forEach((k) => {
403
+ const nv = item[k];
404
+ if (Util.hasOwn(option, k)) {
405
+ const ov = option[k];
406
+ if (ov && typeof ov === 'object') {
407
+ if (nv && typeof nv === 'object' && !Array.isArray(nv)) {
408
+ option[k] = Util.mergeOption(ov, nv);
409
+ return;
410
+ }
411
+ }
412
+ }
413
+ option[k] = nv;
414
+ });
415
+ });
416
+ return option;
417
+ },
418
+
419
+ cmpVersion: (v1, v2) => {
420
+ const [strMajor1, strMinor1, strPatch1] = `${v1}`.split('.');
421
+ const [strMajor2, strMinor2, strPatch2] = `${v2}`.split('.');
422
+ const strList = [strMajor1, strMinor1, strPatch1, strMajor2, strMinor2, strPatch2];
423
+ const list = strList.map((str) => Util.toNum(parseInt(str)));
424
+ const [major1, minor1, patch1, major2, minor2, patch2] = list;
425
+ if (major1 === major2) {
426
+ if (minor1 === minor2) {
427
+ return patch1 - patch2;
428
+ }
429
+ return minor1 - minor2;
430
+ }
431
+ return major1 - major2;
432
+ },
433
+
434
+ // ==========================================================================================
435
+
436
+ loggingLevels: {
437
+ off: 0,
438
+ error: 10,
439
+ info: 20,
440
+ debug: 30
441
+ },
442
+
443
+ initLoggingLevel: (level, from = '') => {
444
+ if (!level && Util.loggingType) {
445
+ return;
446
+ }
447
+ const types = {
448
+ off: 'off',
449
+ error: 'error',
450
+ info: 'info',
451
+ debug: 'debug'
452
+ };
453
+ const type = types[level] || types.info;
454
+ Util.loggingType = type;
455
+ Util.loggingLevel = Util.loggingLevels[type];
456
+
457
+ // console.log('=========================================');
458
+ // console.log(from, Util.loggingType, Util.loggingLevel);
459
+ },
460
+
461
+ logError: (message) => {
462
+ if (Util.loggingLevel < Util.loggingLevels.error) {
463
+ return;
464
+ }
465
+ EC.logRed(`[MR] ${message}`);
466
+ },
467
+
468
+ logInfo: (message) => {
469
+ if (Util.loggingLevel < Util.loggingLevels.info) {
470
+ return;
471
+ }
472
+ console.log(`[MR] ${message}`);
473
+ },
474
+
475
+ // grid is info level
476
+ logGrid: (gridData) => {
477
+ if (Util.loggingLevel < Util.loggingLevels.info) {
478
+ return;
479
+ }
480
+ CG(gridData);
481
+ },
482
+
483
+ logDebug: (message) => {
484
+ if (Util.loggingLevel < Util.loggingLevels.debug) {
485
+ return;
486
+ }
487
+ console.log(`[MR] ${message}`);
488
+ },
489
+
490
+ // time is debug level
491
+ logTime: (message, time_start) => {
492
+ if (Util.loggingLevel < Util.loggingLevels.debug) {
493
+ return;
494
+ }
495
+ const duration = Date.now() - time_start;
496
+ const durationH = Util.TSF(duration);
497
+ const ls = [`[MR] ${message}`, ' ('];
498
+ if (duration <= 100) {
499
+ ls.push(EC.green(durationH));
500
+ } else if (duration < 500) {
501
+ ls.push(EC.yellow(durationH));
502
+ } else {
503
+ ls.push(EC.red(durationH));
504
+ }
505
+ ls.push(')');
506
+ console.log(ls.join(''));
507
+ }
508
+
509
+ };
510
+
511
+ module.exports = Util;