@guritso/terminal 1.2.2 → 1.2.3
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,328 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var url = require('url');
|
|
4
|
+
var fs = require('fs');
|
|
5
|
+
var path = require('path');
|
|
6
|
+
|
|
7
|
+
/*!
|
|
8
|
+
* Terminal
|
|
9
|
+
* Copyright (c) 2024 @guritso
|
|
10
|
+
* @license MIT
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* This function is used to colorize the text
|
|
16
|
+
* @param {string} txt - The text to colorize
|
|
17
|
+
* @returns {string | any} - The colorized text
|
|
18
|
+
* @example
|
|
19
|
+
* color("Hello %H1 World") // "Hello \x1b[1mWorld\x1b[0m"
|
|
20
|
+
* color("%h1 start here%H nothing here") // "\x1b[1m start here\x1b[0m nothing here"
|
|
21
|
+
*/
|
|
22
|
+
function color(txt) {
|
|
23
|
+
if (typeof txt !== "string") return txt;
|
|
24
|
+
|
|
25
|
+
const arr = txt.split("%H");
|
|
26
|
+
let res = arr.join("");
|
|
27
|
+
|
|
28
|
+
for (const a of arr) {
|
|
29
|
+
const [n, ...w] = a.split(" ");
|
|
30
|
+
|
|
31
|
+
if (!n || !txt.includes(`%H${n}`) || isNaN(n)) continue;
|
|
32
|
+
|
|
33
|
+
let code = n;
|
|
34
|
+
if (n >= 40 && n <= 47) {
|
|
35
|
+
code = `90;${n}`;
|
|
36
|
+
} else if (n >= 100 && n <= 107) {
|
|
37
|
+
code = `97;${n}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const mo = `\x1b[${code}m${w.join(" ")}\x1b[0m`;
|
|
41
|
+
|
|
42
|
+
res = res.replace(a, mo);
|
|
43
|
+
}
|
|
44
|
+
return res;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get a list of common error names
|
|
48
|
+
*
|
|
49
|
+
* @returns {string[]}
|
|
50
|
+
*/
|
|
51
|
+
function getErrorNames() {
|
|
52
|
+
return Object.getOwnPropertyNames(global).filter((name) => {
|
|
53
|
+
try {
|
|
54
|
+
return (
|
|
55
|
+
typeof global[name] === "function" &&
|
|
56
|
+
global[name].prototype instanceof Error
|
|
57
|
+
);
|
|
58
|
+
} catch (e) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Format the URL based on the provided host and port.
|
|
65
|
+
*
|
|
66
|
+
* @param {string} host - The host to format.
|
|
67
|
+
* @param {number} port - The port to format.
|
|
68
|
+
* @returns {string} - The formatted URL.
|
|
69
|
+
*/
|
|
70
|
+
function formatUrl(host, port) {
|
|
71
|
+
const locals = ["localhost", "127.0.0.1", "0.0.0.0"];
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
if (typeof host !== "string") {
|
|
75
|
+
return new Error("host must be a string");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const url$1 = new url.URL(host);
|
|
79
|
+
|
|
80
|
+
if (port && port !== url$1.port) {
|
|
81
|
+
url$1.port = port;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!url$1.protocol) {
|
|
85
|
+
url$1.protocol = "http:";
|
|
86
|
+
} else if (url$1.protocol !== "https:" && url$1.protocol !== "http:") {
|
|
87
|
+
url$1.protocol = "http:";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return { url: url$1.toString(), port: url$1.port };
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (locals.includes(host)) {
|
|
93
|
+
return formatUrl("http://localhost", port);
|
|
94
|
+
} else {
|
|
95
|
+
return error;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/*!
|
|
101
|
+
* Terminal
|
|
102
|
+
* Copyright (c) 2024 @guritso
|
|
103
|
+
* @license MIT
|
|
104
|
+
*/
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @typedef {Object} Terminal
|
|
109
|
+
* @property {number} verbose
|
|
110
|
+
* @property {Object} levels
|
|
111
|
+
* @property {string} levels.info
|
|
112
|
+
* @property {string} levels.fail
|
|
113
|
+
* @property {string} levels.pass
|
|
114
|
+
* @property {function(string, number): void} start
|
|
115
|
+
* @property {function(string): void} pass
|
|
116
|
+
* @property {function(string): void} log
|
|
117
|
+
* @property {function(): void} setup
|
|
118
|
+
* @property {function(): void} clear
|
|
119
|
+
* @property {function(number): void} setVerbose
|
|
120
|
+
* @property {function(any): boolean} isError
|
|
121
|
+
* @property {Object} projectInfo
|
|
122
|
+
* @property {function(string): void} loadProjectInfo
|
|
123
|
+
*/
|
|
124
|
+
const terminal = {
|
|
125
|
+
verbose: 2,
|
|
126
|
+
levels: {
|
|
127
|
+
info: color("%H47 INFO %H"),
|
|
128
|
+
fail: color("%H41 FAIL %H"),
|
|
129
|
+
pass: color("%H42 PASS %H"),
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const { stdout } = process;
|
|
134
|
+
|
|
135
|
+
terminal.projectInfo = { name: "unknown", version: "unknown" };
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* display the project info and host, port
|
|
139
|
+
*
|
|
140
|
+
* @param {string} host - The host to display
|
|
141
|
+
* @param {number} port - The port number to display
|
|
142
|
+
*/
|
|
143
|
+
terminal.start = function start(host, port) {
|
|
144
|
+
if (terminal.projectInfo.name === "unknown") {
|
|
145
|
+
terminal.loadProjectInfo();
|
|
146
|
+
}
|
|
147
|
+
const projectInfo = terminal.projectInfo;
|
|
148
|
+
const hostInfo = formatUrl(host, port);
|
|
149
|
+
|
|
150
|
+
const headLines = [
|
|
151
|
+
`\n%H46 name:%H%H33 ${projectInfo.name}`,
|
|
152
|
+
`%H%H31 ${projectInfo.version} %H\n`,
|
|
153
|
+
hostInfo?.url ? `%H43 host:%H %H36 ${hostInfo.url}%H\n` : "",
|
|
154
|
+
hostInfo?.port ? `%H45 port:%H %H31 ${hostInfo.port}%H\n` : "",
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
for (const line of headLines) {
|
|
158
|
+
stdout.write(color(line));
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* display successful messages
|
|
164
|
+
*
|
|
165
|
+
* @param {string} data
|
|
166
|
+
*/
|
|
167
|
+
terminal.pass = function pass(data) {
|
|
168
|
+
if (data === null || data === undefined) return;
|
|
169
|
+
|
|
170
|
+
const { verbose, levels } = terminal;
|
|
171
|
+
|
|
172
|
+
if (verbose === 0) return;
|
|
173
|
+
|
|
174
|
+
if (typeof data === "object") {
|
|
175
|
+
// skipcq: JS-0002
|
|
176
|
+
console.log(color(`\r%H1 ${levels.pass}%H`), data);
|
|
177
|
+
} else {
|
|
178
|
+
terminal.clear();
|
|
179
|
+
stdout.write(color(`\r%H1 ${levels.pass}%H ${String(data)}\n`));
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* display logs messages, error, info, etc.
|
|
185
|
+
*
|
|
186
|
+
* @param {any} data
|
|
187
|
+
*/
|
|
188
|
+
terminal.log = function log(data) {
|
|
189
|
+
const { verbose, levels } = terminal;
|
|
190
|
+
|
|
191
|
+
let level = levels.info;
|
|
192
|
+
|
|
193
|
+
if (verbose === 0) return;
|
|
194
|
+
|
|
195
|
+
if (terminal.isError(data)) {
|
|
196
|
+
level = levels.fail;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (verbose === 1 && stdout.isTTY) {
|
|
200
|
+
terminal.clear();
|
|
201
|
+
|
|
202
|
+
stdout.write(color(`\r%H1 ${level}%H ${String(data)}`));
|
|
203
|
+
} else {
|
|
204
|
+
if (level === terminal.levels.info) {
|
|
205
|
+
if (typeof data === "object") {
|
|
206
|
+
// skipcq: JS-0002
|
|
207
|
+
console.log(color(`%H1 ${level}%H`), data);
|
|
208
|
+
} else {
|
|
209
|
+
stdout.write(color(`%H1 ${level}%H ${String(data)}\n`));
|
|
210
|
+
}
|
|
211
|
+
} else {
|
|
212
|
+
// skipcq: JS-0002
|
|
213
|
+
console.log(color(`%H1 ${level}%H`), data);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
// cache
|
|
218
|
+
let errorKeywords = [];
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Check if the data is an error
|
|
222
|
+
*
|
|
223
|
+
* @param {any} data
|
|
224
|
+
* @returns {boolean}
|
|
225
|
+
*/
|
|
226
|
+
terminal.isError = (data) => {
|
|
227
|
+
if (data instanceof Error) {
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (typeof data === "string") {
|
|
232
|
+
if (!errorKeywords.length) {
|
|
233
|
+
errorKeywords = getErrorNames().map((name) => name);
|
|
234
|
+
errorKeywords.push("Error:");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return errorKeywords.some((keyword) => data.includes(keyword));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return false;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Setup the console.error to use the terminal.log function
|
|
245
|
+
*
|
|
246
|
+
* @returns {boolean} - true if the setup was successful, false otherwise
|
|
247
|
+
*/
|
|
248
|
+
terminal.setup = () => {
|
|
249
|
+
if (typeof console === "object" && console.error) {
|
|
250
|
+
if (typeof console.backup === "function") {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// backup the original console.error
|
|
255
|
+
console.backup = console.error;
|
|
256
|
+
} else {
|
|
257
|
+
throw new Error("console.error is not found");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// replace the console.error with the terminal.log
|
|
261
|
+
console.error = terminal.log;
|
|
262
|
+
|
|
263
|
+
return true;
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Clear the terminal
|
|
268
|
+
*/
|
|
269
|
+
terminal.clear = () => {
|
|
270
|
+
if (stdout.isTTY) {
|
|
271
|
+
stdout.clearLine();
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return false;
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Set the verbose level
|
|
280
|
+
*
|
|
281
|
+
* @param {number} verbose - The verbose level
|
|
282
|
+
*/
|
|
283
|
+
terminal.setVerbose = (verbose) => {
|
|
284
|
+
if (!isNaN(Number(verbose))) {
|
|
285
|
+
const verboseLevel = Number(verbose);
|
|
286
|
+
|
|
287
|
+
if (verboseLevel < 0) {
|
|
288
|
+
terminal.verbose = 0;
|
|
289
|
+
} else if (verboseLevel > 2) {
|
|
290
|
+
terminal.verbose = 2;
|
|
291
|
+
} else {
|
|
292
|
+
terminal.verbose = verboseLevel;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Restore terminal.log function
|
|
299
|
+
* @return {boolean} if true = sucess
|
|
300
|
+
*/
|
|
301
|
+
terminal.teardown = () => {
|
|
302
|
+
if (console.backup) {
|
|
303
|
+
console.error = console.backup;
|
|
304
|
+
delete console.backup;
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
return false;
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Load project info from package.json
|
|
312
|
+
* @param {string} [pkgPath] - Optional path to package.json
|
|
313
|
+
*/
|
|
314
|
+
terminal.loadProjectInfo = (pkgPath) => {
|
|
315
|
+
try {
|
|
316
|
+
const path$1 = pkgPath || path.resolve(process.cwd(), "package.json");
|
|
317
|
+
const pkgData = JSON.parse(fs.readFileSync(path$1, "utf-8"));
|
|
318
|
+
|
|
319
|
+
terminal.projectInfo = {
|
|
320
|
+
name: pkgData.name || "unknown",
|
|
321
|
+
version: pkgData.version || "unknown"
|
|
322
|
+
};
|
|
323
|
+
} catch (error) {
|
|
324
|
+
terminal.projectInfo = { name: "unknown", version: "unknown" };
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
module.exports = terminal;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guritso/terminal",
|
|
3
3
|
"description": "A terminal node utility for enhanced logging and error handling",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.3",
|
|
5
5
|
"main": "index.cjs",
|
|
6
6
|
"module": "index.js",
|
|
7
7
|
"types": "index.d.ts",
|
|
@@ -34,12 +34,16 @@
|
|
|
34
34
|
"jest": "^30.2.0",
|
|
35
35
|
"rollup": "^4.55.3"
|
|
36
36
|
},
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
|
|
37
|
+
"files": [
|
|
38
|
+
"index.js",
|
|
39
|
+
"index.cjs",
|
|
40
|
+
"index.d.ts",
|
|
41
|
+
"lib/",
|
|
42
|
+
"README.md",
|
|
43
|
+
"LICENSE"
|
|
44
|
+
],
|
|
41
45
|
"bugs": {
|
|
42
46
|
"url": "https://github.com/guritso/terminal/issues"
|
|
43
47
|
},
|
|
44
48
|
"homepage": "https://github.com/guritso/terminal#readme"
|
|
45
|
-
}
|
|
49
|
+
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
|
2
|
-
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
|
3
|
-
|
|
4
|
-
name: Node.js CI
|
|
5
|
-
|
|
6
|
-
on:
|
|
7
|
-
push:
|
|
8
|
-
branches: [ "main" ]
|
|
9
|
-
pull_request:
|
|
10
|
-
branches: [ "main" ]
|
|
11
|
-
|
|
12
|
-
jobs:
|
|
13
|
-
build:
|
|
14
|
-
|
|
15
|
-
runs-on: ubuntu-latest
|
|
16
|
-
|
|
17
|
-
strategy:
|
|
18
|
-
matrix:
|
|
19
|
-
node-version: [20.x, 22.x, 24.x]
|
|
20
|
-
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
|
21
|
-
|
|
22
|
-
steps:
|
|
23
|
-
- uses: actions/checkout@v4
|
|
24
|
-
- name: Use Node.js ${{ matrix.node-version }}
|
|
25
|
-
uses: actions/setup-node@v4
|
|
26
|
-
with:
|
|
27
|
-
node-version: ${{ matrix.node-version }}
|
|
28
|
-
cache: 'npm'
|
|
29
|
-
- run: npm ci
|
|
30
|
-
- run: npm run build --if-present
|
|
31
|
-
- run: npm test
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
|
|
2
|
-
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
|
|
3
|
-
|
|
4
|
-
name: Node.js Package
|
|
5
|
-
|
|
6
|
-
on:
|
|
7
|
-
release:
|
|
8
|
-
types: [created]
|
|
9
|
-
|
|
10
|
-
jobs:
|
|
11
|
-
build:
|
|
12
|
-
runs-on: ubuntu-latest
|
|
13
|
-
steps:
|
|
14
|
-
- uses: actions/checkout@v4
|
|
15
|
-
- uses: actions/setup-node@v4
|
|
16
|
-
with:
|
|
17
|
-
node-version: 24.x
|
|
18
|
-
- run: npm ci
|
|
19
|
-
- run: npm run build --if-present
|
|
20
|
-
- run: npm test
|
|
21
|
-
|
|
22
|
-
publish-npm:
|
|
23
|
-
needs: build
|
|
24
|
-
runs-on: ubuntu-latest
|
|
25
|
-
permissions:
|
|
26
|
-
contents: read
|
|
27
|
-
id-token: write
|
|
28
|
-
steps:
|
|
29
|
-
- uses: actions/checkout@v4
|
|
30
|
-
- uses: actions/setup-node@v4
|
|
31
|
-
with:
|
|
32
|
-
node-version: 24.x
|
|
33
|
-
registry-url: https://registry.npmjs.org/
|
|
34
|
-
- run: npm ci
|
|
35
|
-
- run: npm run build --if-present
|
|
36
|
-
- run: npm publish --provenance --access public
|
|
37
|
-
env:
|
|
38
|
-
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
|
Binary file
|
package/rollup.config.js
DELETED
package/tests/terminal.test.js
DELETED
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
const terminal = require("../index.cjs");
|
|
2
|
-
|
|
3
|
-
const levels = terminal.levels;
|
|
4
|
-
|
|
5
|
-
describe("Terminal Module", () => {
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
jest.resetAllMocks();
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
describe("start", () => {
|
|
11
|
-
it("should write project information and port to stdout", () => {
|
|
12
|
-
const mockWrite = jest
|
|
13
|
-
.spyOn(process.stdout, "write")
|
|
14
|
-
.mockImplementation(() => true);
|
|
15
|
-
terminal.projectInfo = { name: "test-project", version: "1.0.0" };
|
|
16
|
-
|
|
17
|
-
terminal.start("localhost", 3000);
|
|
18
|
-
|
|
19
|
-
expect(mockWrite).toHaveBeenCalled();
|
|
20
|
-
mockWrite.mockRestore();
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
describe("pass", () => {
|
|
25
|
-
it("should write success message to stdout", () => {
|
|
26
|
-
const mockWrite = jest
|
|
27
|
-
.spyOn(process.stdout, "write")
|
|
28
|
-
.mockImplementation(() => true);
|
|
29
|
-
terminal.pass("Operation successful");
|
|
30
|
-
|
|
31
|
-
expect(mockWrite).toHaveBeenCalledWith(expect.stringContaining("PASS"));
|
|
32
|
-
mockWrite.mockRestore();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("should use console.log instead of stdout.write when verbose is 2 and msg is an object", () => {
|
|
36
|
-
terminal.setVerbose(2);
|
|
37
|
-
const mockLog = jest.spyOn(console, "log").mockImplementation(() => true);
|
|
38
|
-
const mockWrite = jest
|
|
39
|
-
.spyOn(process.stdout, "write")
|
|
40
|
-
.mockImplementation(() => true);
|
|
41
|
-
|
|
42
|
-
const message = { key: "value" };
|
|
43
|
-
terminal.pass(message);
|
|
44
|
-
|
|
45
|
-
expect(mockLog).toHaveBeenCalledWith(expect.stringContaining(levels.pass), message);
|
|
46
|
-
expect(mockWrite).not.toHaveBeenCalled();
|
|
47
|
-
|
|
48
|
-
mockLog.mockRestore();
|
|
49
|
-
mockWrite.mockRestore();
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
describe("log", () => {
|
|
54
|
-
it("should do nothing if verbose is 0", () => {
|
|
55
|
-
terminal.setVerbose(0);
|
|
56
|
-
const mockWrite = jest
|
|
57
|
-
.spyOn(process.stdout, "write")
|
|
58
|
-
.mockImplementation(() => true);
|
|
59
|
-
|
|
60
|
-
terminal.log("This is a log message");
|
|
61
|
-
terminal.pass("This is a pass message");
|
|
62
|
-
expect(mockWrite).not.toHaveBeenCalled();
|
|
63
|
-
|
|
64
|
-
mockWrite.mockRestore();
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it("should write info message to stdout when verbose is 2 and msg is a string", () => {
|
|
68
|
-
terminal.setVerbose(2);
|
|
69
|
-
const mockWrite = jest
|
|
70
|
-
.spyOn(process.stdout, "write")
|
|
71
|
-
.mockImplementation(() => true);
|
|
72
|
-
|
|
73
|
-
terminal.log("Info message");
|
|
74
|
-
expect(mockWrite).toHaveBeenCalledWith(expect.stringContaining("INFO"));
|
|
75
|
-
mockWrite.mockRestore();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it("should use console.log instead of stdout.write when verbose is 2 and msg is an error string", () => {
|
|
79
|
-
terminal.setVerbose(2);
|
|
80
|
-
const mockLog = jest.spyOn(console, "log").mockImplementation(() => true);
|
|
81
|
-
const mockWrite = jest
|
|
82
|
-
.spyOn(process.stdout, "write")
|
|
83
|
-
.mockImplementation(() => true);
|
|
84
|
-
|
|
85
|
-
const errorMessage = "Error: Something went wrong";
|
|
86
|
-
|
|
87
|
-
terminal.log(errorMessage);
|
|
88
|
-
|
|
89
|
-
expect(mockLog).toHaveBeenCalledWith(expect.stringContaining(levels.fail), errorMessage);
|
|
90
|
-
expect(mockWrite).not.toHaveBeenCalled();
|
|
91
|
-
|
|
92
|
-
mockLog.mockRestore();
|
|
93
|
-
mockWrite.mockRestore();
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("should use console.log instead of stdout.write when verbose is 2 and msg is an object", () => {
|
|
97
|
-
terminal.setVerbose(2);
|
|
98
|
-
const mockLog = jest.spyOn(console, "log").mockImplementation(() => true);
|
|
99
|
-
const mockWrite = jest
|
|
100
|
-
.spyOn(process.stdout, "write")
|
|
101
|
-
.mockImplementation(() => true);
|
|
102
|
-
|
|
103
|
-
const message = { key: "value" };
|
|
104
|
-
terminal.log(message);
|
|
105
|
-
|
|
106
|
-
expect(mockLog).toHaveBeenCalledWith(expect.stringContaining(levels.info), message);
|
|
107
|
-
expect(mockWrite).not.toHaveBeenCalled();
|
|
108
|
-
|
|
109
|
-
mockLog.mockRestore();
|
|
110
|
-
mockWrite.mockRestore();
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
describe("isError", () => {
|
|
115
|
-
it("should return true if the data is an instance of Error", () => {
|
|
116
|
-
const error = new Error("Test error");
|
|
117
|
-
expect(terminal.isError(error)).toBe(true);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it("should return true if the string contains an error keyword", () => {
|
|
121
|
-
expect(terminal.isError("Error: Test error")).toBe(true);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it("should return false for data that is not an error", () => {
|
|
125
|
-
expect(terminal.isError("This is a message")).toBe(false);
|
|
126
|
-
expect(terminal.isError({})).toBe(false);
|
|
127
|
-
expect(terminal.isError(123)).toBe(false);
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
describe("setup", () => {
|
|
132
|
-
it("should replace console.error with terminal.log", () => {
|
|
133
|
-
const originalError = console.error;
|
|
134
|
-
terminal.setup();
|
|
135
|
-
|
|
136
|
-
expect(console.error).toBe(terminal.log);
|
|
137
|
-
|
|
138
|
-
console.error = originalError;
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it("should backup the original console.error", () => {
|
|
142
|
-
const originalError = console.error;
|
|
143
|
-
|
|
144
|
-
terminal.setup();
|
|
145
|
-
|
|
146
|
-
expect(console.backup).toBe(originalError);
|
|
147
|
-
|
|
148
|
-
console.error = originalError;
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it("should not replace console.error if console.backup exists", () => {
|
|
152
|
-
console.backup = jest.fn();
|
|
153
|
-
const originalError = console.error;
|
|
154
|
-
const result = terminal.setup();
|
|
155
|
-
|
|
156
|
-
expect(result).toBe(false);
|
|
157
|
-
expect(console.error).toBe(originalError);
|
|
158
|
-
|
|
159
|
-
delete console.backup;
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it("should throw an error if console.error is not found", () => {
|
|
163
|
-
const originalConsole = global.console;
|
|
164
|
-
global.console = { log: jest.fn() };
|
|
165
|
-
|
|
166
|
-
expect(() => terminal.setup()).toThrow("console.error is not found");
|
|
167
|
-
|
|
168
|
-
global.console = originalConsole;
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
describe("clear", () => {
|
|
173
|
-
it("should call stdout.clearLine if stdout is TTY", () => {
|
|
174
|
-
const originalIsTTY = process.stdout.isTTY;
|
|
175
|
-
process.stdout.isTTY = true;
|
|
176
|
-
|
|
177
|
-
if (!process.stdout.clearLine) {
|
|
178
|
-
process.stdout.clearLine = jest.fn();
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const mockClearLine = jest
|
|
182
|
-
.spyOn(process.stdout, "clearLine")
|
|
183
|
-
.mockImplementation(() => true);
|
|
184
|
-
terminal.clear();
|
|
185
|
-
expect(mockClearLine).toHaveBeenCalled();
|
|
186
|
-
|
|
187
|
-
mockClearLine.mockRestore();
|
|
188
|
-
process.stdout.isTTY = originalIsTTY;
|
|
189
|
-
delete process.stdout.clearLine;
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it("should not call stdout.clearLine if stdout is not TTY", () => {
|
|
193
|
-
const originalIsTTY = process.stdout.isTTY;
|
|
194
|
-
process.stdout.isTTY = false;
|
|
195
|
-
|
|
196
|
-
process.stdout.clearLine = jest.fn();
|
|
197
|
-
|
|
198
|
-
const mockClearLine = jest.spyOn(process.stdout, "clearLine");
|
|
199
|
-
|
|
200
|
-
terminal.clear();
|
|
201
|
-
expect(mockClearLine).not.toHaveBeenCalled();
|
|
202
|
-
|
|
203
|
-
mockClearLine.mockRestore();
|
|
204
|
-
process.stdout.isTTY = originalIsTTY;
|
|
205
|
-
delete process.stdout.clearLine;
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
describe("setVerbose", () => {
|
|
210
|
-
it("should update the verbose level", () => {
|
|
211
|
-
terminal.setVerbose(1);
|
|
212
|
-
expect(terminal.verbose).toBe(1);
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
});
|