@dannysir/js-te 0.1.2 → 0.2.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.
- package/README.md +73 -11
- package/babelTransformImport.js +147 -16
- package/bin/utils/findFiles.js +1 -1
- package/constants.js +2 -1
- package/dist/index.cjs.js +186 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.esm.js +176 -0
- package/dist/index.esm.js.map +1 -0
- package/package.json +28 -23
- package/src/expect.js +21 -0
- package/src/matchers.js +49 -0
- package/src/mock/store.js +1 -1
- package/src/testRunner.js +28 -0
- package/index.js +0 -14
package/README.md
CHANGED
|
@@ -127,16 +127,16 @@ expect(0).toBeFalsy();
|
|
|
127
127
|
|
|
128
128
|
### 동작 원리
|
|
129
129
|
|
|
130
|
-
Babel을 사용해서 import 구문을 변환하여 mock 함수를 가져오도록 했습니다.
|
|
130
|
+
Babel을 사용해서 import/require 구문을 변환하여 mock 함수를 가져오도록 했습니다.
|
|
131
131
|
|
|
132
132
|
1. 테스트 파일 찾기
|
|
133
133
|
1. `mock(path, mockObj)` 선언 확인
|
|
134
134
|
2. `path`를 key로 이용해 Map에 저장
|
|
135
135
|
2. Babel로 코드 변환
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
136
|
+
1. 전체 파일의 import/require문 확인
|
|
137
|
+
2. (0.0.3 버전 추가) import 경로를 **절대 경로**로 변환
|
|
138
|
+
2. import 경로(절대 경로)가 Map에 존재하면 mock 객체로 변환
|
|
139
|
+
3. import 경로(절대 경로)가 Map에 없다면 그대로 import
|
|
140
140
|
3. 테스트 실행
|
|
141
141
|
4. 원본 파일 복구
|
|
142
142
|
|
|
@@ -147,9 +147,46 @@ Babel을 사용해서 import 구문을 변환하여 mock 함수를 가져오도
|
|
|
147
147
|
**🚨 주의사항**
|
|
148
148
|
|
|
149
149
|
1. 반드시 경로는 절대 경로로 입력해주세요.
|
|
150
|
-
|
|
150
|
+
- babel이 import문에서 절대 경로로 변환하여 확인을 하기 때문에 반드시 절대 경로로 등록해주세요.
|
|
151
151
|
2. import문을 반드시 mocking 이후에 선언해주세요.
|
|
152
|
-
|
|
152
|
+
- mocking 전에 import를 하게 되면 mocking되기 전의 모듈을 가져오게 됩니다.
|
|
153
|
+
|
|
154
|
+
**💡 부분 모킹(Partial Mocking)**
|
|
155
|
+
|
|
156
|
+
0.1.3 버전부터 모듈의 일부 함수만 모킹하고 나머지는 원본을 사용할 수 있습니다.
|
|
157
|
+
|
|
158
|
+
```javascript
|
|
159
|
+
// math.js
|
|
160
|
+
export const add = (a, b) => a + b;
|
|
161
|
+
export const subtract = (a, b) => a - b;
|
|
162
|
+
export const multiply = (a, b) => a * b;
|
|
163
|
+
|
|
164
|
+
// math.test.js
|
|
165
|
+
test('부분 모킹 예제', async () => {
|
|
166
|
+
// multiply만 모킹하고 add, subtract는 원본 사용
|
|
167
|
+
mock('/Users/san/Js-Te/math.js', {
|
|
168
|
+
multiply: () => 100 // multiply만 모킹
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const { add, subtract, multiply } = await import('./math.js');
|
|
172
|
+
|
|
173
|
+
expect(add(2, 3)).toBe(5); // 원본 함수 사용
|
|
174
|
+
expect(subtract(5, 3)).toBe(2); // 원본 함수 사용
|
|
175
|
+
expect(multiply(2, 3)).toBe(100); // 모킹된 함수 사용
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**📦 모듈 시스템 지원**
|
|
180
|
+
|
|
181
|
+
ESM(import)과 CommonJS(require) 모두 지원합니다.
|
|
182
|
+
|
|
183
|
+
```javascript
|
|
184
|
+
// ESM 방식
|
|
185
|
+
import { random } from './random.js';
|
|
186
|
+
|
|
187
|
+
// CommonJS 방식
|
|
188
|
+
const { random } = require('./random.js');
|
|
189
|
+
```
|
|
153
190
|
|
|
154
191
|
```javascript
|
|
155
192
|
// random.js
|
|
@@ -157,6 +194,9 @@ export const random = () => Math.random();
|
|
|
157
194
|
|
|
158
195
|
// game.js
|
|
159
196
|
import { random } from './random.js'; // 자유롭게 import하면 babel에서 절대 경로로 변환하여 판단합니다.
|
|
197
|
+
// 또는 CommonJS 방식도 지원
|
|
198
|
+
// const { random } = require('./random.js');
|
|
199
|
+
|
|
160
200
|
export const play = () => random() * 10;
|
|
161
201
|
|
|
162
202
|
// game.test.js
|
|
@@ -166,9 +206,9 @@ test('랜덤 함수 모킹', async () => {
|
|
|
166
206
|
random: () => 0.5
|
|
167
207
|
});
|
|
168
208
|
|
|
169
|
-
// 2. 그 다음 import
|
|
170
|
-
// 상단에 import문을 입력할 경우
|
|
209
|
+
// 2. 그 다음 import (CommonJS도 가능)
|
|
171
210
|
const { play } = await import('./game.js');
|
|
211
|
+
// 또는: const { play } = require('./game.js');
|
|
172
212
|
|
|
173
213
|
// 3. 모킹된 값 사용
|
|
174
214
|
expect(play()).toBe(5);
|
|
@@ -300,6 +340,7 @@ describe('문자열 테스트', () => {
|
|
|
300
340
|
|
|
301
341
|
### 모킹 예제
|
|
302
342
|
|
|
343
|
+
#### 전체 모킹
|
|
303
344
|
```javascript
|
|
304
345
|
// mocking.test.js
|
|
305
346
|
test('[mocking] - mocking random function', async () => {
|
|
@@ -310,7 +351,6 @@ test('[mocking] - mocking random function', async () => {
|
|
|
310
351
|
expect(play()).toBe(30);
|
|
311
352
|
});
|
|
312
353
|
|
|
313
|
-
|
|
314
354
|
// game.js
|
|
315
355
|
import {random} from '/test-helper/random.js'
|
|
316
356
|
|
|
@@ -322,9 +362,31 @@ export const play = () => {
|
|
|
322
362
|
export const random = () => Math.random();
|
|
323
363
|
```
|
|
324
364
|
|
|
365
|
+
#### 부분 모킹
|
|
366
|
+
```javascript
|
|
367
|
+
// calculator.js
|
|
368
|
+
export const add = (a, b) => a + b;
|
|
369
|
+
export const subtract = (a, b) => a - b;
|
|
370
|
+
export const multiply = (a, b) => a * b;
|
|
371
|
+
|
|
372
|
+
// calculator.test.js
|
|
373
|
+
test('[partial mocking] - mock only multiply', async () => {
|
|
374
|
+
// multiply만 모킹, add와 subtract는 원본 사용
|
|
375
|
+
mock('/Users/san/Js-Te/calculator.js', {
|
|
376
|
+
multiply: (a, b) => 999
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
const { add, subtract, multiply } = await import('./calculator.js');
|
|
380
|
+
|
|
381
|
+
expect(add(2, 3)).toBe(5); // 원본: 5
|
|
382
|
+
expect(subtract(5, 2)).toBe(3); // 원본: 3
|
|
383
|
+
expect(multiply(2, 3)).toBe(999); // 모킹: 999
|
|
384
|
+
});
|
|
385
|
+
```
|
|
386
|
+
|
|
325
387
|
## 링크
|
|
326
388
|
|
|
327
|
-
- [GitHub](https://github.com/dannysir/
|
|
389
|
+
- [GitHub](https://github.com/dannysir/Js-Te)
|
|
328
390
|
|
|
329
391
|
## 만든 이유
|
|
330
392
|
|
package/babelTransformImport.js
CHANGED
|
@@ -5,14 +5,16 @@ export const babelTransformImport = ({types: t}) => {
|
|
|
5
5
|
return {
|
|
6
6
|
visitor: {
|
|
7
7
|
Program(path) {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
t.
|
|
11
|
-
t.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
const mockStoreDeclaration = t.VariableDeclaration('const', [
|
|
9
|
+
t.VariableDeclarator(
|
|
10
|
+
t.Identifier(MOCK.STORE_NAME),
|
|
11
|
+
t.MemberExpression(
|
|
12
|
+
t.Identifier('global'),
|
|
13
|
+
t.Identifier(MOCK.STORE_NAME)
|
|
14
|
+
)
|
|
15
|
+
)
|
|
16
|
+
]);
|
|
17
|
+
path.node.body.unshift(mockStoreDeclaration);
|
|
16
18
|
},
|
|
17
19
|
|
|
18
20
|
ImportDeclaration(nodePath, state) {
|
|
@@ -34,8 +36,28 @@ export const babelTransformImport = ({types: t}) => {
|
|
|
34
36
|
|
|
35
37
|
const specifiers = nodePath.node.specifiers;
|
|
36
38
|
|
|
39
|
+
const originalVarName = nodePath.scope.generateUidIdentifier('original');
|
|
37
40
|
const moduleVarName = nodePath.scope.generateUidIdentifier(BABEL.MODULE);
|
|
38
41
|
|
|
42
|
+
/*
|
|
43
|
+
Transformed code for partial mocking support in ESM:
|
|
44
|
+
|
|
45
|
+
const _original = await import('./random.js');
|
|
46
|
+
const _module = mockStore.has('/path/to/random.js')
|
|
47
|
+
? { ..._original, ...mockStore.get('/path/to/random.js') }
|
|
48
|
+
: _original;
|
|
49
|
+
const {random1, random2} = _module;
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
const originalDeclaration = t.variableDeclaration(BABEL.CONST, [
|
|
53
|
+
t.variableDeclarator(
|
|
54
|
+
originalVarName,
|
|
55
|
+
t.awaitExpression(
|
|
56
|
+
t.importExpression(t.stringLiteral(source))
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
]);
|
|
60
|
+
|
|
39
61
|
const moduleDeclaration = t.variableDeclaration(BABEL.CONST, [
|
|
40
62
|
t.variableDeclarator(
|
|
41
63
|
moduleVarName,
|
|
@@ -44,13 +66,16 @@ export const babelTransformImport = ({types: t}) => {
|
|
|
44
66
|
t.memberExpression(t.identifier(MOCK.STORE_NAME), t.identifier(BABEL.HAS)),
|
|
45
67
|
[t.stringLiteral(absolutePath)]
|
|
46
68
|
),
|
|
47
|
-
t.
|
|
48
|
-
t.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
69
|
+
t.objectExpression([
|
|
70
|
+
t.spreadElement(originalVarName),
|
|
71
|
+
t.spreadElement(
|
|
72
|
+
t.callExpression(
|
|
73
|
+
t.memberExpression(t.identifier(MOCK.STORE_NAME), t.identifier(BABEL.GET)),
|
|
74
|
+
[t.stringLiteral(absolutePath)]
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
]),
|
|
78
|
+
originalVarName
|
|
54
79
|
)
|
|
55
80
|
)
|
|
56
81
|
]);
|
|
@@ -80,7 +105,113 @@ export const babelTransformImport = ({types: t}) => {
|
|
|
80
105
|
|
|
81
106
|
const extractDeclaration = t.variableDeclaration(BABEL.CONST, extractDeclarations);
|
|
82
107
|
|
|
83
|
-
nodePath.replaceWithMultiple([moduleDeclaration, extractDeclaration]);
|
|
108
|
+
nodePath.replaceWithMultiple([originalDeclaration, moduleDeclaration, extractDeclaration]);
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
VariableDeclaration(nodePath, state) {
|
|
112
|
+
const declarations = nodePath.node.declarations;
|
|
113
|
+
|
|
114
|
+
if (!declarations || declarations.length === 0) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (nodePath.node._transformed) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const newDeclarations = [];
|
|
123
|
+
let hasTransformation = false;
|
|
124
|
+
|
|
125
|
+
for (const declarator of declarations) {
|
|
126
|
+
const init = declarator.init;
|
|
127
|
+
|
|
128
|
+
if (!init ||
|
|
129
|
+
!t.isCallExpression(init) ||
|
|
130
|
+
!t.isIdentifier(init.callee, { name: 'require' }) ||
|
|
131
|
+
!init.arguments[0] ||
|
|
132
|
+
!t.isStringLiteral(init.arguments[0])) {
|
|
133
|
+
newDeclarations.push(declarator);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
hasTransformation = true;
|
|
138
|
+
const source = init.arguments[0].value;
|
|
139
|
+
const currentFilePath = state.filename || process.cwd();
|
|
140
|
+
const currentDir = path.dirname(currentFilePath);
|
|
141
|
+
|
|
142
|
+
let absolutePath;
|
|
143
|
+
if (source.startsWith(BABEL.PERIOD)) {
|
|
144
|
+
absolutePath = path.resolve(currentDir, source);
|
|
145
|
+
} else {
|
|
146
|
+
absolutePath = source;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/*
|
|
150
|
+
Transformed code for partial mocking support:
|
|
151
|
+
|
|
152
|
+
const _original = require('./random');
|
|
153
|
+
const _module = mockStore.has('/path/to/random.js')
|
|
154
|
+
? { ..._original, ...mockStore.get('/path/to/random.js') }
|
|
155
|
+
: _original;
|
|
156
|
+
*/
|
|
157
|
+
|
|
158
|
+
const originalVar = nodePath.scope.generateUidIdentifier('original');
|
|
159
|
+
const originalDeclarator = t.variableDeclarator(
|
|
160
|
+
originalVar,
|
|
161
|
+
t.callExpression(
|
|
162
|
+
t.identifier('require'),
|
|
163
|
+
[t.stringLiteral(source)]
|
|
164
|
+
)
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const transformedRequire = t.conditionalExpression(
|
|
168
|
+
t.callExpression(
|
|
169
|
+
t.memberExpression(
|
|
170
|
+
t.identifier(MOCK.STORE_NAME),
|
|
171
|
+
t.identifier(BABEL.HAS)
|
|
172
|
+
),
|
|
173
|
+
[t.stringLiteral(absolutePath)]
|
|
174
|
+
),
|
|
175
|
+
t.objectExpression([
|
|
176
|
+
t.spreadElement(originalVar),
|
|
177
|
+
t.spreadElement(
|
|
178
|
+
t.callExpression(
|
|
179
|
+
t.memberExpression(
|
|
180
|
+
t.identifier(MOCK.STORE_NAME),
|
|
181
|
+
t.identifier(BABEL.GET)
|
|
182
|
+
),
|
|
183
|
+
[t.stringLiteral(absolutePath)]
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
]),
|
|
187
|
+
originalVar
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
if (t.isObjectPattern(declarator.id) || t.isArrayPattern(declarator.id)) {
|
|
191
|
+
const tempVar = nodePath.scope.generateUidIdentifier('module');
|
|
192
|
+
|
|
193
|
+
newDeclarations.push(originalDeclarator);
|
|
194
|
+
|
|
195
|
+
newDeclarations.push(
|
|
196
|
+
t.variableDeclarator(tempVar, transformedRequire)
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
newDeclarations.push(
|
|
200
|
+
t.variableDeclarator(declarator.id, tempVar)
|
|
201
|
+
);
|
|
202
|
+
} else {
|
|
203
|
+
newDeclarations.push(originalDeclarator);
|
|
204
|
+
|
|
205
|
+
newDeclarations.push(
|
|
206
|
+
t.variableDeclarator(declarator.id, transformedRequire)
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (hasTransformation) {
|
|
212
|
+
nodePath.node.declarations = newDeclarations;
|
|
213
|
+
nodePath.node._transformed = true;
|
|
214
|
+
}
|
|
84
215
|
}
|
|
85
216
|
}
|
|
86
217
|
};
|
package/bin/utils/findFiles.js
CHANGED
|
@@ -38,7 +38,7 @@ export const findAllSourceFiles = (dir) => {
|
|
|
38
38
|
const items = fs.readdirSync(directory);
|
|
39
39
|
|
|
40
40
|
for (const item of items) {
|
|
41
|
-
if (item === PATH.NODE_MODULES || item === PATH.BIN || item === PATH.TEST_DIRECTORY) continue;
|
|
41
|
+
if (item === PATH.NODE_MODULES || item === PATH.BIN || item === PATH.TEST_DIRECTORY || item === PATH.DIST) continue;
|
|
42
42
|
|
|
43
43
|
const fullPath = path.join(directory, item);
|
|
44
44
|
const stat = fs.statSync(fullPath);
|
package/constants.js
CHANGED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const DIRECTORY_DELIMITER = ' > ';
|
|
4
|
+
|
|
5
|
+
class TestManager {
|
|
6
|
+
#tests = [];
|
|
7
|
+
#testDepth = [];
|
|
8
|
+
#beforeEachArr = [];
|
|
9
|
+
|
|
10
|
+
describe(str, fn) {
|
|
11
|
+
this.#testDepth.push(str);
|
|
12
|
+
const prevLength = this.#beforeEachArr.length;
|
|
13
|
+
fn();
|
|
14
|
+
this.#beforeEachArr.length = prevLength;
|
|
15
|
+
this.#testDepth.pop();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
test(description, fn) {
|
|
19
|
+
const beforeEachHooks = [...this.#beforeEachArr];
|
|
20
|
+
|
|
21
|
+
const testObj = {
|
|
22
|
+
description,
|
|
23
|
+
fn: async () => {
|
|
24
|
+
for (const hook of beforeEachHooks) {
|
|
25
|
+
await hook();
|
|
26
|
+
}
|
|
27
|
+
await fn();
|
|
28
|
+
},
|
|
29
|
+
path: this.#testDepth.join(DIRECTORY_DELIMITER),
|
|
30
|
+
};
|
|
31
|
+
this.#tests.push(testObj);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
testEach(cases) {
|
|
35
|
+
return (description, fn) => {
|
|
36
|
+
cases.forEach(testCase => {
|
|
37
|
+
const args = Array.isArray(testCase) ? testCase : [testCase];
|
|
38
|
+
this.test(this.#formatDescription(args, description), () => fn(...args));
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
beforeEach(fn) {
|
|
44
|
+
this.#beforeEachArr.push(fn);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getTests() {
|
|
48
|
+
return [...this.#tests];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
clearTests() {
|
|
52
|
+
this.#tests = [];
|
|
53
|
+
this.#testDepth = [];
|
|
54
|
+
this.#beforeEachArr = [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#formatDescription(args, description) {
|
|
58
|
+
let argIndex = 0;
|
|
59
|
+
return description.replace(/%([so])/g, (match, type) => {
|
|
60
|
+
if (argIndex >= args.length) return match;
|
|
61
|
+
|
|
62
|
+
const arg = args[argIndex++];
|
|
63
|
+
|
|
64
|
+
switch (type) {
|
|
65
|
+
case 's':
|
|
66
|
+
return arg;
|
|
67
|
+
case 'o':
|
|
68
|
+
return JSON.stringify(arg);
|
|
69
|
+
default:
|
|
70
|
+
return match;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const testManager = new TestManager();
|
|
77
|
+
|
|
78
|
+
const mockStore = new Map();
|
|
79
|
+
|
|
80
|
+
function clearAllMocks() {
|
|
81
|
+
mockStore.clear();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function mock(modulePath, mockExports) {
|
|
85
|
+
mockStore.set(modulePath, mockExports);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function unmock(modulePath) {
|
|
89
|
+
mockStore.delete(modulePath);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function isMocked(modulePath) {
|
|
93
|
+
return mockStore.has(modulePath);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const getErrorMsg = (expect, actual) => {
|
|
97
|
+
return `Expected ${JSON.stringify(expect)} but got ${JSON.stringify(actual)}`;
|
|
98
|
+
};
|
|
99
|
+
const getThrowErrorMsg = (expect) => {
|
|
100
|
+
return `Expected function to throw an error containing "${expect}", but it did not throw`;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const runArgFnc = (actual) => {
|
|
104
|
+
if (typeof actual === 'function') {
|
|
105
|
+
return actual();
|
|
106
|
+
}
|
|
107
|
+
return actual;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const toBe = (actual, expected) => {
|
|
111
|
+
const value = runArgFnc(actual);
|
|
112
|
+
if (value !== expected) {
|
|
113
|
+
throw new Error(getErrorMsg(expected, value));
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const toEqual = (actual, expected) => {
|
|
118
|
+
const value = runArgFnc(actual);
|
|
119
|
+
if (JSON.stringify(value) !== JSON.stringify(expected)) {
|
|
120
|
+
throw new Error(getErrorMsg(expected, value));
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const toThrow = (actual, expected) => {
|
|
125
|
+
try {
|
|
126
|
+
runArgFnc(actual);
|
|
127
|
+
} catch (e) {
|
|
128
|
+
if (!e.message.includes(expected)) {
|
|
129
|
+
throw new Error(getErrorMsg(expected, e.message));
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
throw new Error(getThrowErrorMsg(expected));
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const toBeTruthy = (actual) => {
|
|
137
|
+
const value = runArgFnc(actual);
|
|
138
|
+
if (!value) {
|
|
139
|
+
throw new Error(getErrorMsg(true, value));
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const toBeFalsy = (actual) => {
|
|
144
|
+
const value = runArgFnc(actual);
|
|
145
|
+
if (value) {
|
|
146
|
+
throw new Error(getErrorMsg(false, value));
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const expect = (actual) => {
|
|
151
|
+
return {
|
|
152
|
+
toBe(expected) {
|
|
153
|
+
toBe(actual, expected);
|
|
154
|
+
},
|
|
155
|
+
toEqual(expected) {
|
|
156
|
+
toEqual(actual, expected);
|
|
157
|
+
},
|
|
158
|
+
toThrow(expected) {
|
|
159
|
+
toThrow(actual, expected);
|
|
160
|
+
},
|
|
161
|
+
toBeTruthy() {
|
|
162
|
+
toBeTruthy(actual);
|
|
163
|
+
},
|
|
164
|
+
toBeFalsy() {
|
|
165
|
+
toBeFalsy(actual);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const test = (description, fn) => testManager.test(description, fn);
|
|
171
|
+
test.each = (cases) => testManager.testEach(cases);
|
|
172
|
+
|
|
173
|
+
const describe = (suiteName, fn) => testManager.describe(suiteName, fn);
|
|
174
|
+
|
|
175
|
+
const beforeEach = (fn) => testManager.beforeEach(fn);
|
|
176
|
+
|
|
177
|
+
exports.beforeEach = beforeEach;
|
|
178
|
+
exports.clearAllMocks = clearAllMocks;
|
|
179
|
+
exports.describe = describe;
|
|
180
|
+
exports.expect = expect;
|
|
181
|
+
exports.isMocked = isMocked;
|
|
182
|
+
exports.mock = mock;
|
|
183
|
+
exports.mockStore = mockStore;
|
|
184
|
+
exports.test = test;
|
|
185
|
+
exports.unmock = unmock;
|
|
186
|
+
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../constants.js","../src/testManager.js","../src/mock/store.js","../utils/formatString.js","../src/matchers.js","../src/expect.js","../index.js"],"sourcesContent":["export const RESULT_TITLE = {\n TESTS: 'Tests: ',\n TOTAL : 'Total Result: '\n};\n\nexport const CHECK = '✓ ';\n\nexport const CROSS = '✗ ';\n\nexport const DIRECTORY_DELIMITER = ' > ';\n\nexport const EMPTY = '';\n\nexport const DEFAULT_COUNT = 0;\n\nexport const COLORS = {\n reset: '\\x1b[0m',\n green: '\\x1b[32m',\n red: '\\x1b[31m',\n yellow: '\\x1b[33m',\n cyan: '\\x1b[36m',\n gray: '\\x1b[90m',\n bold: '\\x1b[1m'\n};\n\nexport const MOCK = {\n STORE_NAME: 'mockStore',\n STORE_PATH : '@dannysir/js-te/src/mock/store.js'\n};\n\nexport const BABEL = {\n MODULE: 'module',\n CONST: 'const',\n HAS: 'has',\n GET: 'get',\n PERIOD: '.',\n};\n\nexport const PATH = {\n NODE_MODULES: 'node_modules',\n TEST_DIRECTORY: 'test',\n TEST_FILE: '.test.js',\n JAVASCRIPT_FILE: '.js',\n BIN: 'bin',\n DIST: 'dist',\n};\n","import {DIRECTORY_DELIMITER} from \"../constants.js\";\n\nclass TestManager {\n #tests = [];\n #testDepth = [];\n #beforeEachArr = [];\n\n describe(str, fn) {\n this.#testDepth.push(str);\n const prevLength = this.#beforeEachArr.length;\n fn();\n this.#beforeEachArr.length = prevLength;\n this.#testDepth.pop();\n }\n\n test(description, fn) {\n const beforeEachHooks = [...this.#beforeEachArr];\n\n const testObj = {\n description,\n fn: async () => {\n for (const hook of beforeEachHooks) {\n await hook();\n }\n await fn();\n },\n path: this.#testDepth.join(DIRECTORY_DELIMITER),\n }\n this.#tests.push(testObj);\n }\n\n testEach(cases) {\n return (description, fn) => {\n cases.forEach(testCase => {\n const args = Array.isArray(testCase) ? testCase : [testCase];\n this.test(this.#formatDescription(args, description), () => fn(...args));\n });\n };\n }\n\n beforeEach(fn) {\n this.#beforeEachArr.push(fn);\n }\n\n getTests() {\n return [...this.#tests];\n }\n\n clearTests() {\n this.#tests = [];\n this.#testDepth = [];\n this.#beforeEachArr = [];\n }\n\n #formatDescription(args, description) {\n let argIndex = 0;\n return description.replace(/%([so])/g, (match, type) => {\n if (argIndex >= args.length) return match;\n\n const arg = args[argIndex++];\n\n switch (type) {\n case 's':\n return arg;\n case 'o':\n return JSON.stringify(arg);\n default:\n return match;\n }\n });\n }\n}\n\nexport const testManager = new TestManager();","export const mockStore = new Map();\n\nexport function clearAllMocks() {\n mockStore.clear();\n}\n\nexport function mock(modulePath, mockExports) {\n mockStore.set(modulePath, mockExports);\n}\n\nexport function unmock(modulePath) {\n mockStore.delete(modulePath);\n}\n\nexport function isMocked(modulePath) {\n return mockStore.has(modulePath);\n}\n","import {CHECK, CROSS, DIRECTORY_DELIMITER, EMPTY} from \"../constants.js\";\nimport {green, red} from \"./consoleColor.js\";\n\nexport const formatSuccessMessage = (test) => {\n const pathString = test.path === '' ? EMPTY : test.path + DIRECTORY_DELIMITER;\n return green(CHECK) + pathString + test.description;\n};\n\nexport const formatFailureMessage = (test, error) => {\n const messages = [];\n messages.push(red(CROSS) + test.path + test.description);\n messages.push(red(` Error Message : ${error.message}`));\n return messages.join('\\n');\n};\n\nexport const getErrorMsg = (expect, actual) => {\n return `Expected ${JSON.stringify(expect)} but got ${JSON.stringify(actual)}`;\n};\nexport const getThrowErrorMsg = (expect) => {\n return `Expected function to throw an error containing \"${expect}\", but it did not throw`;\n};","\nimport {getErrorMsg, getThrowErrorMsg} from \"../utils/formatString.js\";\n\nconst runArgFnc = (actual) => {\n if (typeof actual === 'function') {\n return actual();\n }\n return actual;\n};\n\nexport const toBe = (actual, expected) => {\n const value = runArgFnc(actual);\n if (value !== expected) {\n throw new Error(getErrorMsg(expected, value));\n }\n};\n\nexport const toEqual = (actual, expected) => {\n const value = runArgFnc(actual);\n if (JSON.stringify(value) !== JSON.stringify(expected)) {\n throw new Error(getErrorMsg(expected, value));\n }\n};\n\nexport const toThrow = (actual, expected) => {\n try {\n runArgFnc(actual);\n } catch (e) {\n if (!e.message.includes(expected)) {\n throw new Error(getErrorMsg(expected, e.message));\n }\n return;\n }\n throw new Error(getThrowErrorMsg(expected));\n};\n\nexport const toBeTruthy = (actual) => {\n const value = runArgFnc(actual);\n if (!value) {\n throw new Error(getErrorMsg(true, value));\n }\n};\n\nexport const toBeFalsy = (actual) => {\n const value = runArgFnc(actual);\n if (value) {\n throw new Error(getErrorMsg(false, value));\n }\n};\n","import {toBe, toBeFalsy, toBeTruthy, toEqual, toThrow} from \"./matchers.js\";\n\nexport const expect = (actual) => {\n return {\n toBe(expected) {\n toBe(actual, expected);\n },\n toEqual(expected) {\n toEqual(actual, expected);\n },\n toThrow(expected) {\n toThrow(actual, expected);\n },\n toBeTruthy() {\n toBeTruthy(actual);\n },\n toBeFalsy() {\n toBeFalsy(actual);\n }\n }\n};\n","import {testManager} from \"./src/testManager.js\";\nimport {clearAllMocks, isMocked, mock, unmock, mockStore} from './src/mock/store.js';\nimport {expect} from \"./src/expect.js\";\n\nexport const test = (description, fn) => testManager.test(description, fn);\ntest.each = (cases) => testManager.testEach(cases);\n\nexport const describe = (suiteName, fn) => testManager.describe(suiteName, fn);\n\nexport const beforeEach = (fn) => testManager.beforeEach(fn);\n\nexport {expect};\n\nexport {mock, clearAllMocks, unmock, isMocked, mockStore};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
const DIRECTORY_DELIMITER = ' > ';
|
|
2
|
+
|
|
3
|
+
class TestManager {
|
|
4
|
+
#tests = [];
|
|
5
|
+
#testDepth = [];
|
|
6
|
+
#beforeEachArr = [];
|
|
7
|
+
|
|
8
|
+
describe(str, fn) {
|
|
9
|
+
this.#testDepth.push(str);
|
|
10
|
+
const prevLength = this.#beforeEachArr.length;
|
|
11
|
+
fn();
|
|
12
|
+
this.#beforeEachArr.length = prevLength;
|
|
13
|
+
this.#testDepth.pop();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
test(description, fn) {
|
|
17
|
+
const beforeEachHooks = [...this.#beforeEachArr];
|
|
18
|
+
|
|
19
|
+
const testObj = {
|
|
20
|
+
description,
|
|
21
|
+
fn: async () => {
|
|
22
|
+
for (const hook of beforeEachHooks) {
|
|
23
|
+
await hook();
|
|
24
|
+
}
|
|
25
|
+
await fn();
|
|
26
|
+
},
|
|
27
|
+
path: this.#testDepth.join(DIRECTORY_DELIMITER),
|
|
28
|
+
};
|
|
29
|
+
this.#tests.push(testObj);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
testEach(cases) {
|
|
33
|
+
return (description, fn) => {
|
|
34
|
+
cases.forEach(testCase => {
|
|
35
|
+
const args = Array.isArray(testCase) ? testCase : [testCase];
|
|
36
|
+
this.test(this.#formatDescription(args, description), () => fn(...args));
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
beforeEach(fn) {
|
|
42
|
+
this.#beforeEachArr.push(fn);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getTests() {
|
|
46
|
+
return [...this.#tests];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
clearTests() {
|
|
50
|
+
this.#tests = [];
|
|
51
|
+
this.#testDepth = [];
|
|
52
|
+
this.#beforeEachArr = [];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
#formatDescription(args, description) {
|
|
56
|
+
let argIndex = 0;
|
|
57
|
+
return description.replace(/%([so])/g, (match, type) => {
|
|
58
|
+
if (argIndex >= args.length) return match;
|
|
59
|
+
|
|
60
|
+
const arg = args[argIndex++];
|
|
61
|
+
|
|
62
|
+
switch (type) {
|
|
63
|
+
case 's':
|
|
64
|
+
return arg;
|
|
65
|
+
case 'o':
|
|
66
|
+
return JSON.stringify(arg);
|
|
67
|
+
default:
|
|
68
|
+
return match;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const testManager = new TestManager();
|
|
75
|
+
|
|
76
|
+
const mockStore = new Map();
|
|
77
|
+
|
|
78
|
+
function clearAllMocks() {
|
|
79
|
+
mockStore.clear();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function mock(modulePath, mockExports) {
|
|
83
|
+
mockStore.set(modulePath, mockExports);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function unmock(modulePath) {
|
|
87
|
+
mockStore.delete(modulePath);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function isMocked(modulePath) {
|
|
91
|
+
return mockStore.has(modulePath);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const getErrorMsg = (expect, actual) => {
|
|
95
|
+
return `Expected ${JSON.stringify(expect)} but got ${JSON.stringify(actual)}`;
|
|
96
|
+
};
|
|
97
|
+
const getThrowErrorMsg = (expect) => {
|
|
98
|
+
return `Expected function to throw an error containing "${expect}", but it did not throw`;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const runArgFnc = (actual) => {
|
|
102
|
+
if (typeof actual === 'function') {
|
|
103
|
+
return actual();
|
|
104
|
+
}
|
|
105
|
+
return actual;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const toBe = (actual, expected) => {
|
|
109
|
+
const value = runArgFnc(actual);
|
|
110
|
+
if (value !== expected) {
|
|
111
|
+
throw new Error(getErrorMsg(expected, value));
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const toEqual = (actual, expected) => {
|
|
116
|
+
const value = runArgFnc(actual);
|
|
117
|
+
if (JSON.stringify(value) !== JSON.stringify(expected)) {
|
|
118
|
+
throw new Error(getErrorMsg(expected, value));
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const toThrow = (actual, expected) => {
|
|
123
|
+
try {
|
|
124
|
+
runArgFnc(actual);
|
|
125
|
+
} catch (e) {
|
|
126
|
+
if (!e.message.includes(expected)) {
|
|
127
|
+
throw new Error(getErrorMsg(expected, e.message));
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
throw new Error(getThrowErrorMsg(expected));
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const toBeTruthy = (actual) => {
|
|
135
|
+
const value = runArgFnc(actual);
|
|
136
|
+
if (!value) {
|
|
137
|
+
throw new Error(getErrorMsg(true, value));
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const toBeFalsy = (actual) => {
|
|
142
|
+
const value = runArgFnc(actual);
|
|
143
|
+
if (value) {
|
|
144
|
+
throw new Error(getErrorMsg(false, value));
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const expect = (actual) => {
|
|
149
|
+
return {
|
|
150
|
+
toBe(expected) {
|
|
151
|
+
toBe(actual, expected);
|
|
152
|
+
},
|
|
153
|
+
toEqual(expected) {
|
|
154
|
+
toEqual(actual, expected);
|
|
155
|
+
},
|
|
156
|
+
toThrow(expected) {
|
|
157
|
+
toThrow(actual, expected);
|
|
158
|
+
},
|
|
159
|
+
toBeTruthy() {
|
|
160
|
+
toBeTruthy(actual);
|
|
161
|
+
},
|
|
162
|
+
toBeFalsy() {
|
|
163
|
+
toBeFalsy(actual);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const test = (description, fn) => testManager.test(description, fn);
|
|
169
|
+
test.each = (cases) => testManager.testEach(cases);
|
|
170
|
+
|
|
171
|
+
const describe = (suiteName, fn) => testManager.describe(suiteName, fn);
|
|
172
|
+
|
|
173
|
+
const beforeEach = (fn) => testManager.beforeEach(fn);
|
|
174
|
+
|
|
175
|
+
export { beforeEach, clearAllMocks, describe, expect, isMocked, mock, mockStore, test, unmock };
|
|
176
|
+
//# sourceMappingURL=index.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../constants.js","../src/testManager.js","../src/mock/store.js","../utils/formatString.js","../src/matchers.js","../src/expect.js","../index.js"],"sourcesContent":["export const RESULT_TITLE = {\n TESTS: 'Tests: ',\n TOTAL : 'Total Result: '\n};\n\nexport const CHECK = '✓ ';\n\nexport const CROSS = '✗ ';\n\nexport const DIRECTORY_DELIMITER = ' > ';\n\nexport const EMPTY = '';\n\nexport const DEFAULT_COUNT = 0;\n\nexport const COLORS = {\n reset: '\\x1b[0m',\n green: '\\x1b[32m',\n red: '\\x1b[31m',\n yellow: '\\x1b[33m',\n cyan: '\\x1b[36m',\n gray: '\\x1b[90m',\n bold: '\\x1b[1m'\n};\n\nexport const MOCK = {\n STORE_NAME: 'mockStore',\n STORE_PATH : '@dannysir/js-te/src/mock/store.js'\n};\n\nexport const BABEL = {\n MODULE: 'module',\n CONST: 'const',\n HAS: 'has',\n GET: 'get',\n PERIOD: '.',\n};\n\nexport const PATH = {\n NODE_MODULES: 'node_modules',\n TEST_DIRECTORY: 'test',\n TEST_FILE: '.test.js',\n JAVASCRIPT_FILE: '.js',\n BIN: 'bin',\n DIST: 'dist',\n};\n","import {DIRECTORY_DELIMITER} from \"../constants.js\";\n\nclass TestManager {\n #tests = [];\n #testDepth = [];\n #beforeEachArr = [];\n\n describe(str, fn) {\n this.#testDepth.push(str);\n const prevLength = this.#beforeEachArr.length;\n fn();\n this.#beforeEachArr.length = prevLength;\n this.#testDepth.pop();\n }\n\n test(description, fn) {\n const beforeEachHooks = [...this.#beforeEachArr];\n\n const testObj = {\n description,\n fn: async () => {\n for (const hook of beforeEachHooks) {\n await hook();\n }\n await fn();\n },\n path: this.#testDepth.join(DIRECTORY_DELIMITER),\n }\n this.#tests.push(testObj);\n }\n\n testEach(cases) {\n return (description, fn) => {\n cases.forEach(testCase => {\n const args = Array.isArray(testCase) ? testCase : [testCase];\n this.test(this.#formatDescription(args, description), () => fn(...args));\n });\n };\n }\n\n beforeEach(fn) {\n this.#beforeEachArr.push(fn);\n }\n\n getTests() {\n return [...this.#tests];\n }\n\n clearTests() {\n this.#tests = [];\n this.#testDepth = [];\n this.#beforeEachArr = [];\n }\n\n #formatDescription(args, description) {\n let argIndex = 0;\n return description.replace(/%([so])/g, (match, type) => {\n if (argIndex >= args.length) return match;\n\n const arg = args[argIndex++];\n\n switch (type) {\n case 's':\n return arg;\n case 'o':\n return JSON.stringify(arg);\n default:\n return match;\n }\n });\n }\n}\n\nexport const testManager = new TestManager();","export const mockStore = new Map();\n\nexport function clearAllMocks() {\n mockStore.clear();\n}\n\nexport function mock(modulePath, mockExports) {\n mockStore.set(modulePath, mockExports);\n}\n\nexport function unmock(modulePath) {\n mockStore.delete(modulePath);\n}\n\nexport function isMocked(modulePath) {\n return mockStore.has(modulePath);\n}\n","import {CHECK, CROSS, DIRECTORY_DELIMITER, EMPTY} from \"../constants.js\";\nimport {green, red} from \"./consoleColor.js\";\n\nexport const formatSuccessMessage = (test) => {\n const pathString = test.path === '' ? EMPTY : test.path + DIRECTORY_DELIMITER;\n return green(CHECK) + pathString + test.description;\n};\n\nexport const formatFailureMessage = (test, error) => {\n const messages = [];\n messages.push(red(CROSS) + test.path + test.description);\n messages.push(red(` Error Message : ${error.message}`));\n return messages.join('\\n');\n};\n\nexport const getErrorMsg = (expect, actual) => {\n return `Expected ${JSON.stringify(expect)} but got ${JSON.stringify(actual)}`;\n};\nexport const getThrowErrorMsg = (expect) => {\n return `Expected function to throw an error containing \"${expect}\", but it did not throw`;\n};","\nimport {getErrorMsg, getThrowErrorMsg} from \"../utils/formatString.js\";\n\nconst runArgFnc = (actual) => {\n if (typeof actual === 'function') {\n return actual();\n }\n return actual;\n};\n\nexport const toBe = (actual, expected) => {\n const value = runArgFnc(actual);\n if (value !== expected) {\n throw new Error(getErrorMsg(expected, value));\n }\n};\n\nexport const toEqual = (actual, expected) => {\n const value = runArgFnc(actual);\n if (JSON.stringify(value) !== JSON.stringify(expected)) {\n throw new Error(getErrorMsg(expected, value));\n }\n};\n\nexport const toThrow = (actual, expected) => {\n try {\n runArgFnc(actual);\n } catch (e) {\n if (!e.message.includes(expected)) {\n throw new Error(getErrorMsg(expected, e.message));\n }\n return;\n }\n throw new Error(getThrowErrorMsg(expected));\n};\n\nexport const toBeTruthy = (actual) => {\n const value = runArgFnc(actual);\n if (!value) {\n throw new Error(getErrorMsg(true, value));\n }\n};\n\nexport const toBeFalsy = (actual) => {\n const value = runArgFnc(actual);\n if (value) {\n throw new Error(getErrorMsg(false, value));\n }\n};\n","import {toBe, toBeFalsy, toBeTruthy, toEqual, toThrow} from \"./matchers.js\";\n\nexport const expect = (actual) => {\n return {\n toBe(expected) {\n toBe(actual, expected);\n },\n toEqual(expected) {\n toEqual(actual, expected);\n },\n toThrow(expected) {\n toThrow(actual, expected);\n },\n toBeTruthy() {\n toBeTruthy(actual);\n },\n toBeFalsy() {\n toBeFalsy(actual);\n }\n }\n};\n","import {testManager} from \"./src/testManager.js\";\nimport {clearAllMocks, isMocked, mock, unmock, mockStore} from './src/mock/store.js';\nimport {expect} from \"./src/expect.js\";\n\nexport const test = (description, fn) => testManager.test(description, fn);\ntest.each = (cases) => testManager.testEach(cases);\n\nexport const describe = (suiteName, fn) => testManager.describe(suiteName, fn);\n\nexport const beforeEach = (fn) => testManager.beforeEach(fn);\n\nexport {expect};\n\nexport {mock, clearAllMocks, unmock, isMocked, mockStore};\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,23 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dannysir/js-te",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"type": "module",
|
|
3
|
+
"version": "0.2.0",
|
|
5
4
|
"description": "JavaScript test library",
|
|
6
|
-
"main": "index.js",
|
|
5
|
+
"main": "./dist/index.cjs.js",
|
|
6
|
+
"module": "./dist/index.esm.js",
|
|
7
7
|
"exports": {
|
|
8
|
-
".":
|
|
9
|
-
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.esm.js",
|
|
10
|
+
"require": "./dist/index.cjs.js"
|
|
11
|
+
},
|
|
12
|
+
"./src/mock/store.js": {
|
|
13
|
+
"import": "./dist/mock/store.esm.js",
|
|
14
|
+
"require": "./dist/mock/store.cjs.js"
|
|
15
|
+
}
|
|
10
16
|
},
|
|
11
17
|
"bin": {
|
|
12
18
|
"js-te": "./bin/cli.js"
|
|
13
19
|
},
|
|
14
20
|
"scripts": {
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/dannysir/js-te-package.git"
|
|
21
|
+
"build": "rollup -c",
|
|
22
|
+
"test": "node bin/cli.js",
|
|
23
|
+
"prepublishOnly": "npm run build"
|
|
20
24
|
},
|
|
25
|
+
"files": [
|
|
26
|
+
"bin/",
|
|
27
|
+
"dist/",
|
|
28
|
+
"utils/",
|
|
29
|
+
"src/",
|
|
30
|
+
"babelTransformImport.js",
|
|
31
|
+
"constants.js"
|
|
32
|
+
],
|
|
21
33
|
"keywords": [
|
|
22
34
|
"javascript",
|
|
23
35
|
"test",
|
|
@@ -25,25 +37,18 @@
|
|
|
25
37
|
"jest",
|
|
26
38
|
"mocking"
|
|
27
39
|
],
|
|
28
|
-
"files": [
|
|
29
|
-
"bin/",
|
|
30
|
-
"src/testManager.js",
|
|
31
|
-
"src/mock",
|
|
32
|
-
"utils/",
|
|
33
|
-
"index.js",
|
|
34
|
-
"constants.js",
|
|
35
|
-
"babelTransformImport.js"
|
|
36
|
-
],
|
|
37
40
|
"author": "dannysir",
|
|
38
41
|
"license": "ISC",
|
|
39
|
-
"bugs": {
|
|
40
|
-
"url": "https://github.com/dannysir/js-te-package/issues"
|
|
41
|
-
},
|
|
42
|
-
"homepage": "https://github.com/dannysir/js-te-package#readme",
|
|
43
42
|
"dependencies": {
|
|
44
43
|
"@babel/core": "^7.28.5",
|
|
45
44
|
"@babel/generator": "^7.28.5",
|
|
46
45
|
"@babel/parser": "^7.28.5",
|
|
47
46
|
"@babel/traverse": "^7.28.5"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@rollup/plugin-commonjs": "^29.0.0",
|
|
50
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
51
|
+
"rimraf": "^6.1.2",
|
|
52
|
+
"rollup": "^4.53.3"
|
|
48
53
|
}
|
|
49
54
|
}
|
package/src/expect.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {toBe, toBeFalsy, toBeTruthy, toEqual, toThrow} from "./matchers.js";
|
|
2
|
+
|
|
3
|
+
export const expect = (actual) => {
|
|
4
|
+
return {
|
|
5
|
+
toBe(expected) {
|
|
6
|
+
toBe(actual, expected);
|
|
7
|
+
},
|
|
8
|
+
toEqual(expected) {
|
|
9
|
+
toEqual(actual, expected);
|
|
10
|
+
},
|
|
11
|
+
toThrow(expected) {
|
|
12
|
+
toThrow(actual, expected);
|
|
13
|
+
},
|
|
14
|
+
toBeTruthy() {
|
|
15
|
+
toBeTruthy(actual);
|
|
16
|
+
},
|
|
17
|
+
toBeFalsy() {
|
|
18
|
+
toBeFalsy(actual);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
};
|
package/src/matchers.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
|
|
2
|
+
import {getErrorMsg, getThrowErrorMsg} from "../utils/formatString.js";
|
|
3
|
+
|
|
4
|
+
const runArgFnc = (actual) => {
|
|
5
|
+
if (typeof actual === 'function') {
|
|
6
|
+
return actual();
|
|
7
|
+
}
|
|
8
|
+
return actual;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const toBe = (actual, expected) => {
|
|
12
|
+
const value = runArgFnc(actual);
|
|
13
|
+
if (value !== expected) {
|
|
14
|
+
throw new Error(getErrorMsg(expected, value));
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const toEqual = (actual, expected) => {
|
|
19
|
+
const value = runArgFnc(actual);
|
|
20
|
+
if (JSON.stringify(value) !== JSON.stringify(expected)) {
|
|
21
|
+
throw new Error(getErrorMsg(expected, value));
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const toThrow = (actual, expected) => {
|
|
26
|
+
try {
|
|
27
|
+
runArgFnc(actual);
|
|
28
|
+
} catch (e) {
|
|
29
|
+
if (!e.message.includes(expected)) {
|
|
30
|
+
throw new Error(getErrorMsg(expected, e.message));
|
|
31
|
+
}
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
throw new Error(getThrowErrorMsg(expected));
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const toBeTruthy = (actual) => {
|
|
38
|
+
const value = runArgFnc(actual);
|
|
39
|
+
if (!value) {
|
|
40
|
+
throw new Error(getErrorMsg(true, value));
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const toBeFalsy = (actual) => {
|
|
45
|
+
const value = runArgFnc(actual);
|
|
46
|
+
if (value) {
|
|
47
|
+
throw new Error(getErrorMsg(false, value));
|
|
48
|
+
}
|
|
49
|
+
};
|
package/src/mock/store.js
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {DEFAULT_COUNT, RESULT_TITLE} from "../constants.js";
|
|
2
|
+
import {testManager} from "./testManager.js";
|
|
3
|
+
import {formatFailureMessage, formatSuccessMessage} from "../utils/formatString.js";
|
|
4
|
+
import {getTestResultMsg} from "../utils/makeMessage.js";
|
|
5
|
+
import {clearAllMocks} from "./mock/store.js";
|
|
6
|
+
|
|
7
|
+
export const run = async () => {
|
|
8
|
+
let passed = DEFAULT_COUNT;
|
|
9
|
+
let failed = DEFAULT_COUNT;
|
|
10
|
+
|
|
11
|
+
for (const test of testManager.getTests()) {
|
|
12
|
+
try {
|
|
13
|
+
await test.fn();
|
|
14
|
+
console.log(formatSuccessMessage(test));
|
|
15
|
+
passed++;
|
|
16
|
+
clearAllMocks();
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.log(formatFailureMessage(test, error));
|
|
19
|
+
failed++;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log(getTestResultMsg(RESULT_TITLE.TESTS, passed, failed));
|
|
24
|
+
|
|
25
|
+
testManager.clearTests();
|
|
26
|
+
|
|
27
|
+
return {passed, failed};
|
|
28
|
+
};
|
package/index.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import {testManager} from "./src/testManager.js";
|
|
2
|
-
import {clearAllMocks, isMocked, mock, unmock} from './src/mock/store.js';
|
|
3
|
-
import {expect} from "./src/expect.js";
|
|
4
|
-
|
|
5
|
-
export const test = (description, fn) => testManager.test(description, fn);
|
|
6
|
-
test.each = (cases) => testManager.testEach(cases);
|
|
7
|
-
|
|
8
|
-
export const describe = (suiteName, fn) => testManager.describe(suiteName, fn);
|
|
9
|
-
|
|
10
|
-
export const beforeEach = (fn) => testManager.beforeEach(fn);
|
|
11
|
-
|
|
12
|
-
export {expect};
|
|
13
|
-
|
|
14
|
-
export {mock, clearAllMocks, unmock, isMocked};
|