Skip to content

Commit f5397e9

Browse files
committed
Initial commit
1 parent a704762 commit f5397e9

File tree

6 files changed

+5920
-0
lines changed

6 files changed

+5920
-0
lines changed

.babelrc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"presets": [
3+
[
4+
"@babel/preset-env",
5+
{
6+
"loose": true,
7+
"targets": {
8+
"node": "10"
9+
}
10+
}
11+
]
12+
]
13+
}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
/dist

index.js

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { Console } from 'console';
2+
import { Writable } from 'stream';
3+
4+
// Keep an instance of the original console and export it
5+
const originalConsole = global.console;
6+
7+
export { originalConsole };
8+
9+
const instances = new WeakMap();
10+
11+
const LEVELS = {
12+
log: [
13+
'log',
14+
'trace',
15+
'dir',
16+
'dirxml',
17+
'group',
18+
'groupCollapsed',
19+
'debug',
20+
'timeLog',
21+
],
22+
info: ['count', 'info', 'timeEnd'],
23+
warn: ['warn', 'countReset'],
24+
error: ['error', 'assert'],
25+
};
26+
27+
export function createConsole() {
28+
let logs = [];
29+
let levels = {
30+
log: '',
31+
info: '',
32+
warn: '',
33+
error: '',
34+
};
35+
let currentLevel = undefined;
36+
37+
const writable = new Writable({
38+
write(chunk, encoding, callback) {
39+
logs.push([currentLevel, chunk.toString('utf8')]);
40+
if (currentLevel && currentLevel in levels) {
41+
levels[currentLevel] += chunk;
42+
}
43+
callback();
44+
},
45+
});
46+
47+
const jestConsole = new Console({
48+
stdout: writable,
49+
stderr: writable,
50+
});
51+
52+
Object.getOwnPropertyNames(jestConsole).forEach(property => {
53+
if (typeof jestConsole[property] === 'function') {
54+
const originalFunction = jestConsole[property];
55+
jestConsole[property] = function() {
56+
currentLevel = undefined;
57+
Object.keys(LEVELS).forEach(level => {
58+
if (LEVELS[level].includes(property)) {
59+
currentLevel = level;
60+
}
61+
});
62+
63+
return originalFunction.apply(this, arguments);
64+
};
65+
66+
if (typeof jest === 'object' && typeof jest.spyOn === 'function') {
67+
jest.spyOn(jestConsole, property);
68+
}
69+
}
70+
});
71+
72+
instances.set(jestConsole, {
73+
get log() {
74+
return logs.map(log => log[1]).join('');
75+
},
76+
get logs() {
77+
return logs;
78+
},
79+
levels: {
80+
get log() {
81+
return levels.log;
82+
},
83+
get info() {
84+
return levels.info;
85+
},
86+
get warn() {
87+
return levels.warn;
88+
},
89+
get error() {
90+
return levels.error;
91+
},
92+
},
93+
});
94+
95+
return jestConsole;
96+
}
97+
98+
export function mockConsole(jestConsole) {
99+
const originalConsole = global.console;
100+
101+
global.console = jestConsole;
102+
103+
return () => {
104+
global.console = originalConsole;
105+
};
106+
}
107+
108+
export function getLog(jestConsole = global.console) {
109+
return instances.get(jestConsole);
110+
}
111+
112+
if (typeof beforeEach === 'function' && typeof afterEach === 'function') {
113+
let cleanup = () => {};
114+
115+
beforeEach(() => {
116+
cleanup = mockConsole(createConsole());
117+
});
118+
119+
afterEach(cleanup);
120+
}

package.json

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"name": "jest-console",
3+
"version": "0.0.0",
4+
"description": "Console mock for Jest",
5+
"type": "module",
6+
"main": "dist/jest-console.js",
7+
"exports": {
8+
".": {
9+
"require": "dist/jest-console.js",
10+
"default": "index.js"
11+
}
12+
},
13+
"repository": "https://github.com/kevin940726/jest-console.git",
14+
"author": "Kai Hao",
15+
"license": "MIT",
16+
"private": false,
17+
"scripts": {
18+
"build": "babel index.js -o dist/jest-console.js",
19+
"test": "jest"
20+
},
21+
"devDependencies": {
22+
"@babel/cli": "^7.7.4",
23+
"@babel/core": "^7.7.4",
24+
"@babel/preset-env": "^7.7.4",
25+
"canopic": "^0.2.1"
26+
},
27+
"eslintConfig": {
28+
"extends": "react-app"
29+
},
30+
"husky": {
31+
"hooks": {
32+
"pre-commit": "lint-staged"
33+
}
34+
},
35+
"lint-staged": {
36+
"*.{js,jsx,ts,tsx,css,json,md,mdx,html}": [
37+
"prettier --write",
38+
"git add"
39+
],
40+
"*.{js,jsx,ts,tsx}": [
41+
"eslint --fix",
42+
"git add"
43+
]
44+
},
45+
"prettier": {
46+
"printWidth": 80,
47+
"tabWidth": 2,
48+
"useTabs": false,
49+
"semi": true,
50+
"singleQuote": true,
51+
"quoteProps": "as-needed",
52+
"jsxSingleQuote": false,
53+
"trailingComma": "es5",
54+
"bracketSpacing": true,
55+
"jsxBracketSameLine": false,
56+
"arrowParens": "avoid"
57+
}
58+
}

test.js

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
const { getLog } = require('./');
2+
3+
expect(jest.isMockFunction(console.log)).toBe(false);
4+
5+
test('simple', () => {
6+
console.log('Hello %s!', 'World');
7+
8+
expect(getLog().log).toMatchInlineSnapshot(`
9+
"Hello World!
10+
"
11+
`);
12+
});
13+
14+
test('advanced', () => {
15+
console.log(
16+
'It works with inline value: %s, as well as floating point value: %f. Pretty cool!',
17+
'foobar',
18+
3.14
19+
);
20+
console.log(
21+
'Map: %o,\nSet: %o,\nSymbol: %o,\nobject: %o,\nfunction: %o',
22+
new Map([['foo', 42]]),
23+
new Set([42]),
24+
Symbol('symbol'),
25+
{ foo: 42 },
26+
function foo() {
27+
return 42;
28+
}
29+
);
30+
console.error('It works with error too');
31+
console.warn('It works with warning too');
32+
33+
expect(getLog().log).toMatchInlineSnapshot(`
34+
"It works with inline value: foobar, as well as floating point value: 3.14. Pretty cool!
35+
Map: Map { 'foo' => 42, [size]: 1 },
36+
Set: Set { 42, [size]: 1 },
37+
Symbol: Symbol(symbol),
38+
object: { foo: 42 },
39+
function: [Function: foo] {
40+
[length]: 0,
41+
[name]: 'foo',
42+
[prototype]: foo { [constructor]: [Circular] }
43+
}
44+
It works with error too
45+
It works with warning too
46+
"
47+
`);
48+
expect(getLog().logs).toMatchInlineSnapshot(`
49+
Array [
50+
Array [
51+
"log",
52+
"It works with inline value: foobar, as well as floating point value: 3.14. Pretty cool!
53+
",
54+
],
55+
Array [
56+
"log",
57+
"Map: Map { 'foo' => 42, [size]: 1 },
58+
Set: Set { 42, [size]: 1 },
59+
Symbol: Symbol(symbol),
60+
object: { foo: 42 },
61+
function: [Function: foo] {
62+
[length]: 0,
63+
[name]: 'foo',
64+
[prototype]: foo { [constructor]: [Circular] }
65+
}
66+
",
67+
],
68+
Array [
69+
"error",
70+
"It works with error too
71+
",
72+
],
73+
Array [
74+
"warn",
75+
"It works with warning too
76+
",
77+
],
78+
]
79+
`);
80+
expect(getLog().levels.log).toMatchInlineSnapshot(`
81+
"It works with inline value: foobar, as well as floating point value: 3.14. Pretty cool!
82+
Map: Map { 'foo' => 42, [size]: 1 },
83+
Set: Set { 42, [size]: 1 },
84+
Symbol: Symbol(symbol),
85+
object: { foo: 42 },
86+
function: [Function: foo] {
87+
[length]: 0,
88+
[name]: 'foo',
89+
[prototype]: foo { [constructor]: [Circular] }
90+
}
91+
"
92+
`);
93+
expect(getLog().levels.warn).toMatchInlineSnapshot(`
94+
"It works with warning too
95+
"
96+
`);
97+
expect(getLog().levels.error).toMatchInlineSnapshot(`
98+
"It works with error too
99+
"
100+
`);
101+
});
102+
103+
test('they are also mock functions', () => {
104+
console.log.mockImplementationOnce(() => {});
105+
106+
console.log('Will not output');
107+
108+
expect(console.log).toHaveBeenCalledTimes(1);
109+
expect(getLog().log).toBe('');
110+
expect(getLog().logs).toMatchInlineSnapshot(`Array []`);
111+
});

0 commit comments

Comments
 (0)