@dannysir/js-te 0.3.2 → 0.4.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 CHANGED
@@ -3,26 +3,19 @@
3
3
  Jest에서 영감을 받아 만든 가벼운 JavaScript 테스트 프레임워크입니다.
4
4
 
5
5
 
6
- ## [📎 최근 업데이트 0.3.2v](https://github.com/dannysir/js-te-package/blob/main/CHANGELOG.md) & 주요 업데이트 내용
7
-
8
- ### `mock` 이후 import를 해야하는 문제 해결 (0.3.0v)
9
- - 문제 : 기존의 경우 모킹 기능 이용시 반드시 동적 import문을 mock 다음에 작성해야 했음
10
- - 해결
11
- - 기존 `mockStore`를 직접 비교하여 import하는 방식에서 wrapper 패턴을 이용하도록 적용
12
- - 모듈을 새로운 함수로 만들어 함수를 실행할 때마다 `mockStore`와 비교하여 값을 리턴하도록 수정
13
- ### 모듈 변환 최적화 (0.3.0v)
14
- - 문제 : 앞선 변경으로 인해 모든 파일의 모듈들이 사용될 때마다 `mockStore`와 비교하는 로직이 실행됨
15
- - 해결
16
- - `cli.js`로직에 mock 미리 검사하여 mock 경로를 미리 저장하는 로직을 추가
17
- - 미리 확인한 mock 경로를 이용해 import문이 만약 저장된 경로일 때만 babel 변환
18
- ### 리펙토링 (0.3.2v)
19
- - 불필요하게 거대한 로직 분리
20
- - `cli.js` 내부 로직 분리 & `cli.js` 내부에서는 전체 흐름만 관리하도록 수정
21
- - 바벨 플러그인 코드내 중복되는 코드 제거
22
- - 디렉토리 구조 변경
23
- - 기존 디렉토리 내부에 있던 유틸 디렉토리를 통합된 유틸로 관리
24
- - bin 내부에 존재하는 로직을 `cli.js`를 제외하고 전부 src 디렉토리로 옮김
25
- - 분리된 디렉토리를 src 내부에서 관리하도록 수정 (ex: `babelTransformImport.js`)
6
+ ## [📎 최근 업데이트 0.4.0v](https://github.com/dannysir/js-te-package/blob/main/CHANGELOG.md)
7
+
8
+ ### Mock Functions 기능 추가
9
+ - `fn()` 함수로 모킹 가능한 함수 생성
10
+ - Mock function 메서드 지원
11
+ - `mockImplementation()` - 함수 구현 로직 변경
12
+ - `mockReturnValue()` - 고정된 반환값 설정
13
+ - `mockReturnValueOnce()` - 일회성 반환값 설정 (여러 번 호출 가능)
14
+ - `mockClear()` - mock 상태 초기화
15
+ ### Module Mocking 개선
16
+ - `mock()` 함수가 모듈의 모든 함수를 자동으로 mock function으로 변환
17
+ - 변환된 함수들도 `mockImplementation()`, `mockReturnValue()` 사용 가능
18
+
26
19
  ---
27
20
  ## 설치
28
21
 
@@ -36,7 +29,7 @@ npm install --save-dev @dannysir/js-te
36
29
 
37
30
  `*.test.js` 파일을 만들면 자동으로 찾아서 실행합니다.
38
31
 
39
- 별도의 import문 없이 `describe`와 `test`, `expect` 로직이 사용 가능합니다.
32
+ 별도의 import문 없이 `describe`와 `test`, `expect` 로직이 사용 가능합니다.
40
33
 
41
34
  ```javascript
42
35
  // math.test.js
@@ -177,18 +170,188 @@ const {random1, random2} = _module;
177
170
  3. 테스트 실행
178
171
  4. 원본 파일 복구
179
172
 
180
- ### `mock(모듈 절대 경로), mock객체)`
173
+ ### Mock Functions
174
+
175
+ **0.4.0 버전부터 Jest와 유사한 Mock Functions 기능을 제공합니다.**
176
+
177
+ `fn()` 함수로 생성한 mock function은 반환값 제어 등의 기능을 제공합니다.
178
+
179
+ ### `fn(implementation : optional)`
180
+
181
+ 모킹 가능한 함수를 생성합니다.
182
+
183
+ ```javascript
184
+ import { fn } from '@dannysir/js-te';
185
+
186
+ test('mock function 기본 사용', () => {
187
+ const mockFn = fn();
188
+
189
+ mockFn('test');
190
+ mockFn(1, 2, 3);
191
+
192
+ // mock 함수는 기본적으로 undefined 반환
193
+ expect(mockFn()).toBe(undefined);
194
+ });
195
+
196
+ test('초기 구현과 함께 생성', () => {
197
+ const mockFn = fn((x, y) => x + y);
198
+
199
+ expect(mockFn(1, 2)).toBe(3);
200
+ });
201
+ ```
202
+
203
+ ### `mockImplementation(fn)`
204
+
205
+ Mock 함수의 구현을 변경합니다.
206
+
207
+ ```javascript
208
+ test('구현 변경하기', () => {
209
+ const mockFn = fn();
210
+
211
+ mockFn.mockImplementation((x) => x * 2);
212
+
213
+ expect(mockFn(5)).toBe(10);
214
+ });
215
+ ```
216
+
217
+ ### `mockReturnValue(value)`
218
+
219
+ Mock 함수가 항상 특정 값을 반환하도록 설정합니다.
220
+
221
+ ```javascript
222
+ test('고정 반환값 설정', () => {
223
+ const mockFn = fn();
224
+
225
+ mockFn.mockReturnValue(42);
226
+
227
+ expect(mockFn()).toBe(42);
228
+ expect(mockFn()).toBe(42);
229
+ expect(mockFn()).toBe(42);
230
+ });
231
+ ```
232
+
233
+ ### `mockReturnValueOnce(...values)`
234
+
235
+ Mock 함수가 지정된 값들을 순서대로 한 번씩 반환하도록 설정합니다.
236
+
237
+ ```javascript
238
+ test('일회성 반환값 설정', () => {
239
+ const mockFn = fn();
240
+
241
+ mockFn.mockReturnValueOnce(1, 2, 3);
242
+
243
+ expect(mockFn()).toBe(1);
244
+ expect(mockFn()).toBe(2);
245
+ expect(mockFn()).toBe(3);
246
+ expect(mockFn()).toBe(undefined); // 큐가 비면 기본값 반환
247
+ });
248
+
249
+ test('mockReturnValueOnce와 mockReturnValue 조합', () => {
250
+ const mockFn = fn();
251
+
252
+ mockFn
253
+ .mockReturnValueOnce(1, 2)
254
+ .mockReturnValue(99);
255
+
256
+ expect(mockFn()).toBe(1);
257
+ expect(mockFn()).toBe(2);
258
+ expect(mockFn()).toBe(99); // 이후 계속 99 반환
259
+ expect(mockFn()).toBe(99);
260
+ });
261
+ ```
262
+
263
+ ### `mockClear()`
264
+
265
+ Mock 함수의 상태를 초기화합니다.
266
+
267
+ ```javascript
268
+ test('mock 상태 초기화', () => {
269
+ const mockFn = fn();
270
+
271
+ mockFn.mockReturnValue(42);
272
+ expect(mockFn()).toBe(42);
273
+
274
+ mockFn.mockClear();
275
+ expect(mockFn()).toBe(undefined); // 기본값으로 돌아감
276
+ });
277
+ ```
278
+
279
+ ### Module Mocking
280
+
281
+ #### `mock(모듈 절대 경로), mock객체)`
181
282
 
182
283
  모듈을 모킹합니다. import 하기 **전에** 호출해야 합니다.
183
284
 
184
- **🚨 주의사항**
285
+ > **0.4.0 버전부터 `mock()` 함수가 모듈의 모든 함수를 자동으로 mock function으로 변환합니다.**
286
+ >
287
+ > **0.4.0 버전부터 `mock()` 함수가 모킹 객체를 리턴합니다.**
185
288
 
186
- 1. 반드시 경로는 절대 경로로 입력해주세요.
289
+ ### **🚨 주의사항 (매우 중요)**
290
+
291
+ 1. **반드시 경로는 절대 경로로 입력해주세요.**
187
292
  - babel이 import문에서 절대 경로로 변환하여 확인을 하기 때문에 반드시 절대 경로로 등록해주세요.
188
293
  2. ~~import문을 반드시 mocking 이후에 선언해주세요.~~
189
294
  - ~~mocking 전에 import를 하게 되면 mocking되기 전의 모듈을 가져오게 됩니다.~~
190
295
 
191
296
  > **0.3.0 버전부터 import문을 mock선언 이후에 하지 않아도 됩니다.**
297
+ 3. **모킹한 모듈을 제어하고 싶다면 반드시 리턴 받은 객체를 활용하세요.**
298
+
299
+ **반환값 사용 예시**
300
+ ```javascript
301
+ // math.js
302
+ export const add = (a, b) => a + b;
303
+ export const multiply = (a, b) => a * b;
304
+
305
+ // math.test.js
306
+ import { add, multiply} from './math.js';
307
+ test('mock 객체 반환값 활용', () => {
308
+ // mock() 함수가 모킹된 객체를 반환
309
+ // example - { add : 모킹함수, multiply : 모킹함수}
310
+ const mockedMath = mock('/absolute/path/to/math.js', {
311
+ add: (a, b) => a + b,
312
+ multiply: (a, b) => a * b
313
+ });
314
+
315
+ // ⚠️ 중요: mock function 메서드는 반드시 반환받은 객체에 사용하세요
316
+ // 올바른 사용 ✅
317
+ mockedMath.add.mockReturnValue(100);
318
+ mockedMath.multiply.mockReturnValueOnce(50, 75);
319
+ // 잘못된 사용 ❌
320
+ // add.mockReturnValue(100); // 동작하지 않습니다!
321
+
322
+ expect(add(1, 2)).toBe(100);
323
+ expect(multiply(2, 3)).toBe(50);
324
+ expect(multiply(2, 3)).toBe(75);
325
+
326
+ });
327
+ ```
328
+
329
+ ### **왜 반환된 객체를 사용해야 하나요?**
330
+
331
+ **간략 설명**
332
+
333
+ wrapper 패턴을 통해 모듈을 변경하기 때문에 mock function에 접근이 불가
334
+
335
+ **상세 설명**
336
+
337
+ `mock()` 함수는 모듈의 함수들을 mock function으로 변환하여 mockStore에 저장합니다.
338
+
339
+ 하지만 `import`로 가져온 함수는 wrapper 함수이기 때문에 mock function의 메서드(`mockReturnValue`, `mockImplementation` 등)를 직접 가지고 있지 않습니다.
340
+
341
+ 따라서 mock function의 메서드를 사용하려면 **반드시 `mock()` 함수가 반환한 객체**를 통해 접근해야 합니다.
342
+ ```javascript
343
+ // 동작 원리
344
+ const mockedMath = mock('/path/to/math.js', {
345
+ add: (a, b) => a + b
346
+ });
347
+
348
+ // mockedMath.add는 실제 mock function (메서드 있음) ✅
349
+ mockedMath.add.mockReturnValue(100);
350
+
351
+ // import로 가져온 add는 wrapper 함수 (메서드 없음) ❌
352
+ const { add } = await import('./math.js');
353
+ // add.mockReturnValue(100); // TypeError: add.mockReturnValue is not a function
354
+ ```
192
355
 
193
356
  **💡 부분 모킹(Partial Mocking)**
194
357
 
@@ -214,6 +377,8 @@ test('부분 모킹 예제', async () => {
214
377
  });
215
378
  ```
216
379
 
380
+ > 모킹을 한 모듈에 mock function을 쓰고 싶으면
381
+
217
382
  **📦 모듈 시스템 지원**
218
383
 
219
384
  ESM(import)과 CommonJS(require) 모두 지원합니다.
@@ -249,12 +414,32 @@ test('랜덤 함수 모킹', async () => {
249
414
  // 2. 모킹된 값 사용
250
415
  expect(play()).toBe(5);
251
416
  });
417
+
418
+ // 0.4.0 버전부터 mock functions 사용 가능
419
+ test('mock functions로 동적 제어', async () => {
420
+ const mocked = mock('/Users/san/Js-Te/test-helper/random.js', {
421
+ random: () => 0.5
422
+ });
423
+
424
+ expect(play()).toBe(5);
425
+
426
+ // 반환값 동적 변경
427
+ mocked.random.mockReturnValue(0.3);
428
+ expect(play()).toBe(3);
429
+
430
+ // 일회성 반환값 설정
431
+ mocked.random.mockReturnValueOnce(0.1);
432
+ expect(play()).toBe(1);
433
+ expect(play()).toBe(3); // 이전 설정값으로 복귀
434
+ });
252
435
  ```
253
436
 
254
437
  ### `clearAllMocks()`
255
438
 
256
439
  등록된 모든 mock을 제거합니다.
257
440
 
441
+ **참고**: `clearAllMocks()`는 mockStore에서 mock을 제거하지만, 각 mock function의 내부 상태(returnQueue, implementation 등)는 초기화하지 않습니다. Mock function의 상태를 초기화하려면 각 함수의 `mockClear()` 메서드를 사용하세요.
442
+
258
443
  ### `unmock(모듈경로)`
259
444
 
260
445
  특정 mock만 제거합니다.
@@ -374,20 +559,38 @@ describe('문자열 테스트', () => {
374
559
  });
375
560
  ```
376
561
 
377
- ### 모킹 예제
562
+ ### Mock Functions 기본 사용
563
+ ```javascript
564
+ import { fn } from '@dannysir/js-te';
565
+
566
+ test('콜백 함수 모킹', () => {
567
+ const mockCallback = fn((x) => x * 2);
568
+
569
+ expect(mockCallback(21)).toBe(42);
570
+
571
+ // 구현 변경
572
+ mockCallback.mockImplementation((x) => x + 10);
573
+ expect(mockCallback(5)).toBe(15);
574
+ });
575
+ ```
378
576
 
379
- #### 전체 모킹
577
+ ### 전체 모킹
578
+ #### 모듈 모킹 (전체)
380
579
  ```javascript
381
580
  // mocking.test.js
382
581
  import {random} from '../src/test-helper/game.js'; // 0.2.4 버전부터 import문 상단 배치 가능
383
582
 
384
583
  test('[mocking] - mocking random function', async () => {
385
- mock('/Users/san/Js-Te/test-helper/random.js', {
584
+ const mocked = mock('/Users/san/Js-Te/test-helper/random.js', {
386
585
  random: () => 3,
387
586
  });
388
587
  // 0.3.0 버전 이전까지는 반드시 mock 이후 동적 import문 작성
389
588
  // const {play} = await import('../src/test-helper/game.js');
390
589
  expect(play()).toBe(30);
590
+
591
+ // 0.4.0 버전부터: 동적으로 반환값 변경 가능
592
+ mocked.random.mockReturnValue(5);
593
+ expect(play()).toBe(50);
391
594
  });
392
595
 
393
596
  // game.js
@@ -401,7 +604,7 @@ export const play = () => {
401
604
  export const random = () => Math.random();
402
605
  ```
403
606
 
404
- #### 부분 모킹
607
+ #### 모듈 모킹 (부분)
405
608
  ```javascript
406
609
  // calculator.js
407
610
  export const add = (a, b) => a + b;
@@ -411,7 +614,7 @@ export const multiply = (a, b) => a * b;
411
614
  // calculator.test.js
412
615
  test('[partial mocking] - mock only multiply', async () => {
413
616
  // multiply만 모킹, add와 subtract는 원본 사용
414
- mock('/Users/san/Js-Te/calculator.js', {
617
+ const mocked = mock('/Users/san/Js-Te/calculator.js', {
415
618
  multiply: (a, b) => 999
416
619
  });
417
620
 
@@ -419,7 +622,17 @@ test('[partial mocking] - mock only multiply', async () => {
419
622
 
420
623
  expect(add(2, 3)).toBe(5);
421
624
  expect(subtract(5, 2)).toBe(3);
422
- expect(multiply(2, 3)).toBe(999);
625
+ expect(multiply(2, 3)).toBe(999);
626
+
627
+ // 0.4.0 버전부터: mock function 메서드 사용 가능
628
+ mocked.multiply.mockReturnValue(100);
629
+ expect(multiply(2, 3)).toBe(100);
630
+
631
+ // 일회성 반환값
632
+ mocked.multiply.mockReturnValueOnce(50, 75);
633
+ expect(multiply(2, 3)).toBe(50);
634
+ expect(multiply(2, 3)).toBe(75);
635
+ expect(multiply(2, 3)).toBe(100); // 이전 설정값으로 복귀
423
636
  });
424
637
  ```
425
638
 
package/bin/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import {NUM, RESULT_MSG} from "../src/constants/index.js";
4
- import {restoreFiles} from "../src/utils/transformFiles.js";
5
- import {getErrorMsgInLogic, getFileCountString, getTestResultMsg} from "../src/utils/messages.js";
4
+ import {restoreFiles} from "../src/cli/utils/transformFiles.js";
5
+ import {getErrorMsgInLogic, getFileCountString, getTestResultMsg} from "../src/cli/utils/messages.js";
6
6
  import {setupEnvironment} from "../src/cli/setupEnvironment.js";
7
7
  import {setupFiles} from "../src/cli/setupFiles.js";
8
8
  import {runTests} from "../src/cli/runTests.js";
@@ -26,4 +26,4 @@ const main = async () => {
26
26
  };
27
27
 
28
28
  const exitCode = await main();
29
- process.exit(exitCode);
29
+ process.exit(exitCode);
package/dist/index.cjs CHANGED
@@ -36,21 +36,85 @@ const formatFailureMessage = (test, error) => {
36
36
  return messages.join('\n');
37
37
  };
38
38
 
39
- const getErrorMsg = (expect, actual) => {
39
+ const formatErrorMsg = (expect, actual) => {
40
40
  return `Expected ${JSON.stringify(expect)} but got ${JSON.stringify(actual)}`;
41
41
  };
42
42
 
43
- const getThrowErrorMsg = (expect) => {
43
+ const formatThrowErrorMsg = (expect) => {
44
44
  return `Expected function to throw an error containing "${expect}", but it did not throw`;
45
45
  };
46
46
 
47
- const placeHolder = {
48
- 's': (value) => value,
49
- 'o': (value) => JSON.stringify(value),
47
+ /**
48
+ * 목업 함수의 메서드 타입 정의
49
+ * @typedef {Object} MockMethods
50
+ * @property {function(function): MockMethods} mockImplementation - 구현 설정
51
+ * @property {function(...*): MockMethods} mockReturnValueOnce - 일회성 반환값 설정
52
+ * @property {function(*): MockMethods} mockReturnValue - 반환값 설정
53
+ * @property {function(): void} mockClear - 목업 상태 초기화
54
+ */
55
+
56
+ /**
57
+ * @param {function} mockFn
58
+ * @param {Object} state
59
+ * @returns {{mockImplementation(*): this, mockReturnValueOnce(...[*]): this, mockReturnValue(*): this, mockClear(): this}|*}
60
+ */
61
+ const mockFunctions = (mockFn, state) => {
62
+ return {
63
+ mockImplementation(newImpl) {
64
+ state.curImplement = newImpl;
65
+ return mockFn;
66
+ },
67
+
68
+ mockReturnValueOnce(...value) {
69
+ state.returnQueue.push(...value);
70
+ return mockFn;
71
+ },
72
+
73
+ mockReturnValue(value) {
74
+ state.curImplement = () => value;
75
+ return mockFn;
76
+ },
77
+
78
+ mockClear() {
79
+ state.returnQueue = [];
80
+ state.curImplement = () => null;
81
+ return mockFn;
82
+ },
83
+ }
50
84
  };
51
85
 
52
- const getMatcherForReplace = () => {
53
- return new RegExp(`%([${Object.keys(placeHolder).join('')}])`, 'g')
86
+ /**
87
+ * 목업 함수로 변환하는 로직
88
+ * @param {function} [implementation] - 목업 함수 로직 (선택, 기본값: () => null)
89
+ * @returns {(function(...*): *) & MockMethods} 호출 가능하면서 목업 메서드를 가진 함수
90
+ */
91
+ const makeMockFnc = (implementation = (() => null)) => {
92
+ const state = {
93
+ returnQueue: [],
94
+ curImplement: implementation,
95
+ };
96
+
97
+ const mockFn = (...args) => {
98
+ if (state.returnQueue.length > 0) {
99
+ return state.returnQueue.shift();
100
+ }
101
+ return state.curImplement(...args);
102
+ };
103
+
104
+ const methods = mockFunctions(mockFn, state);
105
+ Object.assign(mockFn, methods);
106
+
107
+ return mockFn;
108
+ };
109
+
110
+ const changeModuleExports = (moduleExports) => {
111
+ const result = {};
112
+
113
+ for (const moduleName in moduleExports) {
114
+ result[moduleName] = makeMockFnc(moduleExports[moduleName]);
115
+ }
116
+
117
+ return result;
54
118
  };
55
119
 
56
120
  const mockStore = new Map();
@@ -60,7 +124,9 @@ const clearAllMocks = () => {
60
124
  };
61
125
 
62
126
  const mock = (modulePath, mockExports) => {
63
- mockStore.set(modulePath, mockExports);
127
+ const mockedExports = changeModuleExports(mockExports);
128
+ mockStore.set(modulePath, mockedExports);
129
+ return mockStore.get(modulePath);
64
130
  };
65
131
 
66
132
  const unmock = (modulePath) => {
@@ -88,6 +154,10 @@ class TestManager {
88
154
  #tests = [];
89
155
  #testDepth = [];
90
156
  #beforeEachArr = [];
157
+ #placeHolder = {
158
+ 's': (value) => value,
159
+ 'o': (value) => JSON.stringify(value),
160
+ };
91
161
 
92
162
  describe(str, fn) {
93
163
  this.#testDepth.push(str);
@@ -159,12 +229,16 @@ class TestManager {
159
229
  return {passed, failed};
160
230
  }
161
231
 
232
+ #getMatcherForReplace = () => {
233
+ return new RegExp(`%([${Object.keys(this.#placeHolder).join('')}])`, 'g')
234
+ };
235
+
162
236
  #formatDescription(args, description) {
163
237
  let argIndex = 0;
164
- return description.replace(getMatcherForReplace(), (match, type) => {
238
+ return description.replace(this.#getMatcherForReplace(), (match, type) => {
165
239
  if (argIndex >= args.length) return match;
166
240
 
167
- const formatter = placeHolder[type];
241
+ const formatter = this.#placeHolder[type];
168
242
 
169
243
  return formatter ? formatter(args[argIndex++]) : match;
170
244
  });
@@ -183,14 +257,14 @@ const runArgFnc = (actual) => {
183
257
  const toBe = (actual, expected) => {
184
258
  const value = runArgFnc(actual);
185
259
  if (value !== expected) {
186
- throw new Error(getErrorMsg(expected, value));
260
+ throw new Error(formatErrorMsg(expected, value));
187
261
  }
188
262
  };
189
263
 
190
264
  const toEqual = (actual, expected) => {
191
265
  const value = runArgFnc(actual);
192
266
  if (JSON.stringify(value) !== JSON.stringify(expected)) {
193
- throw new Error(getErrorMsg(expected, value));
267
+ throw new Error(formatErrorMsg(expected, value));
194
268
  }
195
269
  };
196
270
 
@@ -199,24 +273,24 @@ const toThrow = (actual, expected) => {
199
273
  runArgFnc(actual);
200
274
  } catch (e) {
201
275
  if (!e.message.includes(expected)) {
202
- throw new Error(getErrorMsg(expected, e.message));
276
+ throw new Error(formatErrorMsg(expected, e.message));
203
277
  }
204
278
  return;
205
279
  }
206
- throw new Error(getThrowErrorMsg(expected));
280
+ throw new Error(formatThrowErrorMsg(expected));
207
281
  };
208
282
 
209
283
  const toBeTruthy = (actual) => {
210
284
  const value = runArgFnc(actual);
211
285
  if (!value) {
212
- throw new Error(getErrorMsg(true, value));
286
+ throw new Error(formatErrorMsg(true, value));
213
287
  }
214
288
  };
215
289
 
216
290
  const toBeFalsy = (actual) => {
217
291
  const value = runArgFnc(actual);
218
292
  if (value) {
219
- throw new Error(getErrorMsg(false, value));
293
+ throw new Error(formatErrorMsg(false, value));
220
294
  }
221
295
  };
222
296
 
@@ -310,10 +384,13 @@ const beforeEach = (fn) => testManager.beforeEach(fn);
310
384
  */
311
385
  const run = () => testManager.run();
312
386
 
387
+ const fn = makeMockFnc;
388
+
313
389
  exports.beforeEach = beforeEach;
314
390
  exports.clearAllMocks = clearAllMocks;
315
391
  exports.describe = describe;
316
392
  exports.expect = expect;
393
+ exports.fn = fn;
317
394
  exports.isMocked = isMocked;
318
395
  exports.mock = mock;
319
396
  exports.mockStore = mockStore;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/constants/view.js","../src/utils/consoleColor.js","../src/constants/index.js","../src/utils/formatString.js","../src/mock/store.js","../src/utils/messages.js","../src/testManager.js","../src/matchers.js","../src/expect.js","../index.js"],"sourcesContent":["export 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","import {COLORS} from \"../constants/view.js\";\n\nexport const colorize = (text, color) => `${color}${text}${COLORS.reset}`;\n\nexport const green = (text) => colorize(text, COLORS.green + COLORS.bold);\nexport const red = (text) => colorize(text, COLORS.red + COLORS.bold);\nexport const bold = (text) => colorize(text, COLORS.bold);\nexport const yellow = (text) => colorize(text, COLORS.yellow + COLORS.bold);\n","export 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 DANNYSIR_JS_TE: '@dannysir/js-te',\n};\n\nexport const RESULT_MSG = {\n TESTS: 'Tests: ',\n TOTAL: 'Total Result: ',\n CHECK: '✓ ',\n CROSS: '✗ ',\n DIRECTORY_DELIMITER: ' > ',\n EMPTY: '',\n};\n\nexport const NUM = {\n ZERO: 0,\n ONE: 1,\n};\n\nexport const MODULE_TYPE = {\n MODULE: 'module',\n ESM: 'esm',\n CJS: 'cjs',\n};\n","import {green, red} from \"./consoleColor.js\";\nimport {RESULT_MSG} from \"../constants/index.js\";\n\nexport const formatSuccessMessage = (test) => {\n const pathString = test.path === '' ? RESULT_MSG.EMPTY : test.path + RESULT_MSG.DIRECTORY_DELIMITER;\n return green(RESULT_MSG.CHECK) + pathString + test.description;\n};\n\nexport const formatFailureMessage = (test, error) => {\n const messages = [];\n messages.push(red(RESULT_MSG.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};\n\nexport const getThrowErrorMsg = (expect) => {\n return `Expected function to throw an error containing \"${expect}\", but it did not throw`;\n};\n\nexport const placeHolder = {\n 's': (value) => value,\n 'o': (value) => JSON.stringify(value),\n};\n\nexport const getMatcherForReplace = () => {\n return new RegExp(`%([${Object.keys(placeHolder).join('')}])`, 'g')\n};\n","export const mockStore = new Map();\n\nexport const clearAllMocks = () => {\n mockStore.clear();\n}\n\nexport const mock = (modulePath, mockExports) => {\n mockStore.set(modulePath, mockExports);\n}\n\nexport const unmock = (modulePath) => {\n mockStore.delete(modulePath);\n}\n\nexport const isMocked = (modulePath) => {\n return mockStore.has(modulePath);\n}\n","import {bold, green, red, yellow} from \"./consoleColor.js\";\n\nexport const getTestResultMsg = (title, success, fail) => {\n let msg = '\\n';\n\n msg += title;\n msg += green(success + ' passed') + ', ';\n if (fail) {\n msg += red(fail + ' failed') + ', ';\n }\n msg += bold(success + fail + ' total');\n\n return msg;\n};\n\nexport const getFileCountString = (n) => {\n return `\\nFound ${green(n)} test file(s)`;\n};\n\nexport const getFilePath = (path) => {\n return `\\n${yellow(path)}\\n`;\n};\n\nexport const getErrorMsgInLogic = (error) => {\n return red(`\\n✗ Test execution failed\\n Error: ${error}\\n`)\n};\n","import {formatFailureMessage, formatSuccessMessage, getMatcherForReplace, placeHolder} from \"./utils/formatString.js\";\nimport {clearAllMocks} from \"./mock/store.js\";\nimport {NUM, RESULT_MSG} from \"./constants/index.js\";\nimport {getTestResultMsg} from \"./utils/messages.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(RESULT_MSG.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 async run() {\n let passed = NUM.ZERO;\n let failed = NUM.ZERO;\n\n for (const test of testManager.getTests()) {\n try {\n await test.fn();\n console.log(formatSuccessMessage(test));\n passed++;\n clearAllMocks();\n } catch (error) {\n console.log(formatFailureMessage(test, error));\n failed++;\n }\n }\n\n console.log(getTestResultMsg(RESULT_MSG.TESTS, passed, failed));\n\n testManager.clearTests();\n\n return {passed, failed};\n }\n\n #formatDescription(args, description) {\n let argIndex = 0;\n return description.replace(getMatcherForReplace(), (match, type) => {\n if (argIndex >= args.length) return match;\n\n const formatter = placeHolder[type];\n\n return formatter ? formatter(args[argIndex++]) : match;\n });\n }\n}\n\nexport const testManager = new TestManager();","import {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\n/**\n * 테스트 케이스를 정의합니다.\n * @param {string} description - 테스트 설명\n * @param {Function} fn - 테스트 함수\n *\n * @example\n * test('더하기 테스트', () => {\n * expect(1 + 2).toBe(3);\n * });\n */\nexport const test = (description, fn) => testManager.test(description, fn);\n\n/**\n * 배열 형태의 테스트 케이스를 반복 실행합니다.\n * @param {Array<Array>} cases - 테스트 케이스 배열\n * @returns {Function} 테스트 실행 함수\n *\n * @example\n * test.each([\n * [1, 2, 3],\n * [2, 3, 5],\n * ])('add(%s, %s) = %s', (a, b, expected) => {\n * expect(a + b).toBe(expected);\n * });\n */\ntest.each = (cases) => testManager.testEach(cases);\n\n/**\n * 테스트 그룹을 정의합니다. 중첩 가능합니다.\n * @param {string} suiteName - 그룹 이름\n * @param {Function} fn - 그룹 내부 테스트들을 정의하는 함수\n *\n * @example\n * describe('계산기', () => {\n * test('더하기', () => {\n * expect(1 + 1).toBe(2);\n * });\n * });\n */\nexport const describe = (suiteName, fn) => testManager.describe(suiteName, fn);\n\n/**\n * 각 테스트 실행 전에 실행될 함수를 등록합니다.\n * @param {Function} fn - 전처리 함수\n *\n * @example\n * describe('카운터 테스트', () => {\n * let counter;\n *\n * beforeEach(() => {\n * counter = 0;\n * });\n *\n * test('초기값은 0', () => {\n * expect(counter).toBe(0);\n * });\n * });\n */\nexport const beforeEach = (fn) => testManager.beforeEach(fn);\n\n/**\n * 등록된 모든 테스트를 실행합니다.\n * @returns {Promise<{passed: number, failed: number}>} 테스트 결과\n *\n * @example\n * const { passed, failed } = await run();\n * console.log(`${passed} passed, ${failed} failed`);\n */\nexport const run = () => testManager.run();\n\n/**\n * 값을 검증하는 matcher 함수들을 반환합니다.\n * @function\n * @param {*} actual - 검증할 값\n * @returns {Object} matcher 함수들\n *\n * @example\n * expect(1 + 1).toBe(2);\n * expect([1, 2, 3]).toEqual([1, 2, 3]);\n * expect(() => { throw new Error('error') }).toThrow('error');\n * expect(true).toBeTruthy();\n * expect(false).toBeFalsy();\n */\nexport {expect};\n\nexport {mock, clearAllMocks, unmock, isMocked, mockStore};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/constants/view.js","../src/utils/consoleColor.js","../src/constants/index.js","../src/utils/formatString.js","../src/mock/utils/mockFunctions.js","../src/mock/utils/changeModuleExports.js","../src/mock/store.js","../src/cli/utils/messages.js","../src/testManager.js","../src/matchers.js","../src/expect.js","../index.js"],"sourcesContent":["export 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","import {COLORS} from \"../constants/view.js\";\n\nexport const colorize = (text, color) => `${color}${text}${COLORS.reset}`;\n\nexport const green = (text) => colorize(text, COLORS.green + COLORS.bold);\nexport const red = (text) => colorize(text, COLORS.red + COLORS.bold);\nexport const bold = (text) => colorize(text, COLORS.bold);\nexport const yellow = (text) => colorize(text, COLORS.yellow + COLORS.bold);\n","export 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 DANNYSIR_JS_TE: '@dannysir/js-te',\n};\n\nexport const RESULT_MSG = {\n TESTS: 'Tests: ',\n TOTAL: 'Total Result: ',\n CHECK: '✓ ',\n CROSS: '✗ ',\n DIRECTORY_DELIMITER: ' > ',\n EMPTY: '',\n};\n\nexport const NUM = {\n ZERO: 0,\n ONE: 1,\n};\n\nexport const MODULE_TYPE = {\n MODULE: 'module',\n ESM: 'esm',\n CJS: 'cjs',\n};\n","import {green, red} from \"./consoleColor.js\";\nimport {RESULT_MSG} from \"../constants/index.js\";\n\nexport const formatSuccessMessage = (test) => {\n const pathString = test.path === '' ? RESULT_MSG.EMPTY : test.path + RESULT_MSG.DIRECTORY_DELIMITER;\n return green(RESULT_MSG.CHECK) + pathString + test.description;\n};\n\nexport const formatFailureMessage = (test, error) => {\n const messages = [];\n messages.push(red(RESULT_MSG.CROSS) + test.path + test.description);\n messages.push(red(` Error Message : ${error.message}`));\n return messages.join('\\n');\n};\n\nexport const formatErrorMsg = (expect, actual) => {\n return `Expected ${JSON.stringify(expect)} but got ${JSON.stringify(actual)}`;\n};\n\nexport const formatThrowErrorMsg = (expect) => {\n return `Expected function to throw an error containing \"${expect}\", but it did not throw`;\n};\n","/**\n * 목업 함수의 메서드 타입 정의\n * @typedef {Object} MockMethods\n * @property {function(function): MockMethods} mockImplementation - 구현 설정\n * @property {function(...*): MockMethods} mockReturnValueOnce - 일회성 반환값 설정\n * @property {function(*): MockMethods} mockReturnValue - 반환값 설정\n * @property {function(): void} mockClear - 목업 상태 초기화\n */\n\n/**\n * @param {function} mockFn\n * @param {Object} state\n * @returns {{mockImplementation(*): this, mockReturnValueOnce(...[*]): this, mockReturnValue(*): this, mockClear(): this}|*}\n */\nexport const mockFunctions = (mockFn, state) => {\n return {\n mockImplementation(newImpl) {\n state.curImplement = newImpl;\n return mockFn;\n },\n\n mockReturnValueOnce(...value) {\n state.returnQueue.push(...value);\n return mockFn;\n },\n\n mockReturnValue(value) {\n state.curImplement = () => value;\n return mockFn;\n },\n\n mockClear() {\n state.returnQueue = [];\n state.curImplement = () => null;\n return mockFn;\n },\n }\n};\n","import {mockFunctions} from \"./mockFunctions.js\";\n\n/**\n * 목업 함수로 변환하는 로직\n * @param {function} [implementation] - 목업 함수 로직 (선택, 기본값: () => null)\n * @returns {(function(...*): *) & MockMethods} 호출 가능하면서 목업 메서드를 가진 함수\n */\nexport const makeMockFnc = (implementation = (() => null)) => {\n const state = {\n returnQueue: [],\n curImplement: implementation,\n };\n\n const mockFn = (...args) => {\n if (state.returnQueue.length > 0) {\n return state.returnQueue.shift();\n }\n return state.curImplement(...args);\n };\n\n const methods = mockFunctions(mockFn, state);\n Object.assign(mockFn, methods);\n\n return mockFn;\n};\n\nexport const changeModuleExports = (moduleExports) => {\n const result = {};\n\n for (const moduleName in moduleExports) {\n result[moduleName] = makeMockFnc(moduleExports[moduleName]);\n }\n\n return result;\n};\n","import {changeModuleExports} from \"./utils/changeModuleExports.js\";\n\nexport const mockStore = new Map();\n\nexport const clearAllMocks = () => {\n mockStore.clear();\n}\n\nexport const mock = (modulePath, mockExports) => {\n const mockedExports = changeModuleExports(mockExports);\n mockStore.set(modulePath, mockedExports);\n return mockStore.get(modulePath);\n}\n\nexport const unmock = (modulePath) => {\n mockStore.delete(modulePath);\n}\n\nexport const isMocked = (modulePath) => {\n return mockStore.has(modulePath);\n}\n","import {bold, green, red, yellow} from \"../../utils/consoleColor.js\";\n\nexport const getTestResultMsg = (title, success, fail) => {\n let msg = '\\n';\n\n msg += title;\n msg += green(success + ' passed') + ', ';\n if (fail) {\n msg += red(fail + ' failed') + ', ';\n }\n msg += bold(success + fail + ' total');\n\n return msg;\n};\n\nexport const getFileCountString = (n) => {\n return `\\nFound ${green(n)} test file(s)`;\n};\n\nexport const getFilePath = (path) => {\n return `\\n${yellow(path)}\\n`;\n};\n\nexport const getErrorMsgInLogic = (error) => {\n return red(`\\n✗ Test execution failed\\n Error: ${error}\\n`)\n};\n","import {formatFailureMessage, formatSuccessMessage} from \"./utils/formatString.js\";\nimport {clearAllMocks} from \"./mock/store.js\";\nimport {NUM, RESULT_MSG} from \"./constants/index.js\";\nimport {getTestResultMsg} from \"./cli/utils/messages.js\";\n\nclass TestManager {\n #tests = [];\n #testDepth = [];\n #beforeEachArr = [];\n #placeHolder = {\n 's': (value) => value,\n 'o': (value) => JSON.stringify(value),\n };\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(RESULT_MSG.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 async run() {\n let passed = NUM.ZERO;\n let failed = NUM.ZERO;\n\n for (const test of testManager.getTests()) {\n try {\n await test.fn();\n console.log(formatSuccessMessage(test));\n passed++;\n clearAllMocks();\n } catch (error) {\n console.log(formatFailureMessage(test, error));\n failed++;\n }\n }\n\n console.log(getTestResultMsg(RESULT_MSG.TESTS, passed, failed));\n\n testManager.clearTests();\n\n return {passed, failed};\n }\n\n #getMatcherForReplace = () => {\n return new RegExp(`%([${Object.keys(this.#placeHolder).join('')}])`, 'g')\n };\n\n #formatDescription(args, description) {\n let argIndex = 0;\n return description.replace(this.#getMatcherForReplace(), (match, type) => {\n if (argIndex >= args.length) return match;\n\n const formatter = this.#placeHolder[type];\n\n return formatter ? formatter(args[argIndex++]) : match;\n });\n }\n}\n\nexport const testManager = new TestManager();","import {formatErrorMsg, formatThrowErrorMsg} 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(formatErrorMsg(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(formatErrorMsg(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(formatErrorMsg(expected, e.message));\n }\n return;\n }\n throw new Error(formatThrowErrorMsg(expected));\n};\n\nexport const toBeTruthy = (actual) => {\n const value = runArgFnc(actual);\n if (!value) {\n throw new Error(formatErrorMsg(true, value));\n }\n};\n\nexport const toBeFalsy = (actual) => {\n const value = runArgFnc(actual);\n if (value) {\n throw new Error(formatErrorMsg(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\";\nimport {makeMockFnc} from \"./src/mock/utils/changeModuleExports.js\";\n\n/**\n * 테스트 케이스를 정의합니다.\n * @param {string} description - 테스트 설명\n * @param {Function} fn - 테스트 함수\n *\n * @example\n * test('더하기 테스트', () => {\n * expect(1 + 2).toBe(3);\n * });\n */\nexport const test = (description, fn) => testManager.test(description, fn);\n\n/**\n * 배열 형태의 테스트 케이스를 반복 실행합니다.\n * @param {Array<Array>} cases - 테스트 케이스 배열\n * @returns {Function} 테스트 실행 함수\n *\n * @example\n * test.each([\n * [1, 2, 3],\n * [2, 3, 5],\n * ])('add(%s, %s) = %s', (a, b, expected) => {\n * expect(a + b).toBe(expected);\n * });\n */\ntest.each = (cases) => testManager.testEach(cases);\n\n/**\n * 테스트 그룹을 정의합니다. 중첩 가능합니다.\n * @param {string} suiteName - 그룹 이름\n * @param {Function} fn - 그룹 내부 테스트들을 정의하는 함수\n *\n * @example\n * describe('계산기', () => {\n * test('더하기', () => {\n * expect(1 + 1).toBe(2);\n * });\n * });\n */\nexport const describe = (suiteName, fn) => testManager.describe(suiteName, fn);\n\n/**\n * 각 테스트 실행 전에 실행될 함수를 등록합니다.\n * @param {Function} fn - 전처리 함수\n *\n * @example\n * describe('카운터 테스트', () => {\n * let counter;\n *\n * beforeEach(() => {\n * counter = 0;\n * });\n *\n * test('초기값은 0', () => {\n * expect(counter).toBe(0);\n * });\n * });\n */\nexport const beforeEach = (fn) => testManager.beforeEach(fn);\n\n/**\n * 등록된 모든 테스트를 실행합니다.\n * @returns {Promise<{passed: number, failed: number}>} 테스트 결과\n *\n * @example\n * const { passed, failed } = await run();\n * console.log(`${passed} passed, ${failed} failed`);\n */\nexport const run = () => testManager.run();\n\n/**\n * 값을 검증하는 matcher 함수들을 반환합니다.\n * @function\n * @param {*} actual - 검증할 값\n * @returns {Object} matcher 함수들\n *\n * @example\n * expect(1 + 1).toBe(2);\n * expect([1, 2, 3]).toEqual([1, 2, 3]);\n * expect(() => { throw new Error('error') }).toThrow('error');\n * expect(true).toBeTruthy();\n * expect(false).toBeFalsy();\n */\nexport {expect};\n\nexport {mock, clearAllMocks, unmock, isMocked, mockStore};\n\nexport const fn = makeMockFnc;\n"],"names":[],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -34,21 +34,85 @@ const formatFailureMessage = (test, error) => {
34
34
  return messages.join('\n');
35
35
  };
36
36
 
37
- const getErrorMsg = (expect, actual) => {
37
+ const formatErrorMsg = (expect, actual) => {
38
38
  return `Expected ${JSON.stringify(expect)} but got ${JSON.stringify(actual)}`;
39
39
  };
40
40
 
41
- const getThrowErrorMsg = (expect) => {
41
+ const formatThrowErrorMsg = (expect) => {
42
42
  return `Expected function to throw an error containing "${expect}", but it did not throw`;
43
43
  };
44
44
 
45
- const placeHolder = {
46
- 's': (value) => value,
47
- 'o': (value) => JSON.stringify(value),
45
+ /**
46
+ * 목업 함수의 메서드 타입 정의
47
+ * @typedef {Object} MockMethods
48
+ * @property {function(function): MockMethods} mockImplementation - 구현 설정
49
+ * @property {function(...*): MockMethods} mockReturnValueOnce - 일회성 반환값 설정
50
+ * @property {function(*): MockMethods} mockReturnValue - 반환값 설정
51
+ * @property {function(): void} mockClear - 목업 상태 초기화
52
+ */
53
+
54
+ /**
55
+ * @param {function} mockFn
56
+ * @param {Object} state
57
+ * @returns {{mockImplementation(*): this, mockReturnValueOnce(...[*]): this, mockReturnValue(*): this, mockClear(): this}|*}
58
+ */
59
+ const mockFunctions = (mockFn, state) => {
60
+ return {
61
+ mockImplementation(newImpl) {
62
+ state.curImplement = newImpl;
63
+ return mockFn;
64
+ },
65
+
66
+ mockReturnValueOnce(...value) {
67
+ state.returnQueue.push(...value);
68
+ return mockFn;
69
+ },
70
+
71
+ mockReturnValue(value) {
72
+ state.curImplement = () => value;
73
+ return mockFn;
74
+ },
75
+
76
+ mockClear() {
77
+ state.returnQueue = [];
78
+ state.curImplement = () => null;
79
+ return mockFn;
80
+ },
81
+ }
48
82
  };
49
83
 
50
- const getMatcherForReplace = () => {
51
- return new RegExp(`%([${Object.keys(placeHolder).join('')}])`, 'g')
84
+ /**
85
+ * 목업 함수로 변환하는 로직
86
+ * @param {function} [implementation] - 목업 함수 로직 (선택, 기본값: () => null)
87
+ * @returns {(function(...*): *) & MockMethods} 호출 가능하면서 목업 메서드를 가진 함수
88
+ */
89
+ const makeMockFnc = (implementation = (() => null)) => {
90
+ const state = {
91
+ returnQueue: [],
92
+ curImplement: implementation,
93
+ };
94
+
95
+ const mockFn = (...args) => {
96
+ if (state.returnQueue.length > 0) {
97
+ return state.returnQueue.shift();
98
+ }
99
+ return state.curImplement(...args);
100
+ };
101
+
102
+ const methods = mockFunctions(mockFn, state);
103
+ Object.assign(mockFn, methods);
104
+
105
+ return mockFn;
106
+ };
107
+
108
+ const changeModuleExports = (moduleExports) => {
109
+ const result = {};
110
+
111
+ for (const moduleName in moduleExports) {
112
+ result[moduleName] = makeMockFnc(moduleExports[moduleName]);
113
+ }
114
+
115
+ return result;
52
116
  };
53
117
 
54
118
  const mockStore = new Map();
@@ -58,7 +122,9 @@ const clearAllMocks = () => {
58
122
  };
59
123
 
60
124
  const mock = (modulePath, mockExports) => {
61
- mockStore.set(modulePath, mockExports);
125
+ const mockedExports = changeModuleExports(mockExports);
126
+ mockStore.set(modulePath, mockedExports);
127
+ return mockStore.get(modulePath);
62
128
  };
63
129
 
64
130
  const unmock = (modulePath) => {
@@ -86,6 +152,10 @@ class TestManager {
86
152
  #tests = [];
87
153
  #testDepth = [];
88
154
  #beforeEachArr = [];
155
+ #placeHolder = {
156
+ 's': (value) => value,
157
+ 'o': (value) => JSON.stringify(value),
158
+ };
89
159
 
90
160
  describe(str, fn) {
91
161
  this.#testDepth.push(str);
@@ -157,12 +227,16 @@ class TestManager {
157
227
  return {passed, failed};
158
228
  }
159
229
 
230
+ #getMatcherForReplace = () => {
231
+ return new RegExp(`%([${Object.keys(this.#placeHolder).join('')}])`, 'g')
232
+ };
233
+
160
234
  #formatDescription(args, description) {
161
235
  let argIndex = 0;
162
- return description.replace(getMatcherForReplace(), (match, type) => {
236
+ return description.replace(this.#getMatcherForReplace(), (match, type) => {
163
237
  if (argIndex >= args.length) return match;
164
238
 
165
- const formatter = placeHolder[type];
239
+ const formatter = this.#placeHolder[type];
166
240
 
167
241
  return formatter ? formatter(args[argIndex++]) : match;
168
242
  });
@@ -181,14 +255,14 @@ const runArgFnc = (actual) => {
181
255
  const toBe = (actual, expected) => {
182
256
  const value = runArgFnc(actual);
183
257
  if (value !== expected) {
184
- throw new Error(getErrorMsg(expected, value));
258
+ throw new Error(formatErrorMsg(expected, value));
185
259
  }
186
260
  };
187
261
 
188
262
  const toEqual = (actual, expected) => {
189
263
  const value = runArgFnc(actual);
190
264
  if (JSON.stringify(value) !== JSON.stringify(expected)) {
191
- throw new Error(getErrorMsg(expected, value));
265
+ throw new Error(formatErrorMsg(expected, value));
192
266
  }
193
267
  };
194
268
 
@@ -197,24 +271,24 @@ const toThrow = (actual, expected) => {
197
271
  runArgFnc(actual);
198
272
  } catch (e) {
199
273
  if (!e.message.includes(expected)) {
200
- throw new Error(getErrorMsg(expected, e.message));
274
+ throw new Error(formatErrorMsg(expected, e.message));
201
275
  }
202
276
  return;
203
277
  }
204
- throw new Error(getThrowErrorMsg(expected));
278
+ throw new Error(formatThrowErrorMsg(expected));
205
279
  };
206
280
 
207
281
  const toBeTruthy = (actual) => {
208
282
  const value = runArgFnc(actual);
209
283
  if (!value) {
210
- throw new Error(getErrorMsg(true, value));
284
+ throw new Error(formatErrorMsg(true, value));
211
285
  }
212
286
  };
213
287
 
214
288
  const toBeFalsy = (actual) => {
215
289
  const value = runArgFnc(actual);
216
290
  if (value) {
217
- throw new Error(getErrorMsg(false, value));
291
+ throw new Error(formatErrorMsg(false, value));
218
292
  }
219
293
  };
220
294
 
@@ -308,5 +382,7 @@ const beforeEach = (fn) => testManager.beforeEach(fn);
308
382
  */
309
383
  const run = () => testManager.run();
310
384
 
311
- export { beforeEach, clearAllMocks, describe, expect, isMocked, mock, mockStore, run, test, unmock };
385
+ const fn = makeMockFnc;
386
+
387
+ export { beforeEach, clearAllMocks, describe, expect, fn, isMocked, mock, mockStore, run, test, unmock };
312
388
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/constants/view.js","../src/utils/consoleColor.js","../src/constants/index.js","../src/utils/formatString.js","../src/mock/store.js","../src/utils/messages.js","../src/testManager.js","../src/matchers.js","../src/expect.js","../index.js"],"sourcesContent":["export 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","import {COLORS} from \"../constants/view.js\";\n\nexport const colorize = (text, color) => `${color}${text}${COLORS.reset}`;\n\nexport const green = (text) => colorize(text, COLORS.green + COLORS.bold);\nexport const red = (text) => colorize(text, COLORS.red + COLORS.bold);\nexport const bold = (text) => colorize(text, COLORS.bold);\nexport const yellow = (text) => colorize(text, COLORS.yellow + COLORS.bold);\n","export 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 DANNYSIR_JS_TE: '@dannysir/js-te',\n};\n\nexport const RESULT_MSG = {\n TESTS: 'Tests: ',\n TOTAL: 'Total Result: ',\n CHECK: '✓ ',\n CROSS: '✗ ',\n DIRECTORY_DELIMITER: ' > ',\n EMPTY: '',\n};\n\nexport const NUM = {\n ZERO: 0,\n ONE: 1,\n};\n\nexport const MODULE_TYPE = {\n MODULE: 'module',\n ESM: 'esm',\n CJS: 'cjs',\n};\n","import {green, red} from \"./consoleColor.js\";\nimport {RESULT_MSG} from \"../constants/index.js\";\n\nexport const formatSuccessMessage = (test) => {\n const pathString = test.path === '' ? RESULT_MSG.EMPTY : test.path + RESULT_MSG.DIRECTORY_DELIMITER;\n return green(RESULT_MSG.CHECK) + pathString + test.description;\n};\n\nexport const formatFailureMessage = (test, error) => {\n const messages = [];\n messages.push(red(RESULT_MSG.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};\n\nexport const getThrowErrorMsg = (expect) => {\n return `Expected function to throw an error containing \"${expect}\", but it did not throw`;\n};\n\nexport const placeHolder = {\n 's': (value) => value,\n 'o': (value) => JSON.stringify(value),\n};\n\nexport const getMatcherForReplace = () => {\n return new RegExp(`%([${Object.keys(placeHolder).join('')}])`, 'g')\n};\n","export const mockStore = new Map();\n\nexport const clearAllMocks = () => {\n mockStore.clear();\n}\n\nexport const mock = (modulePath, mockExports) => {\n mockStore.set(modulePath, mockExports);\n}\n\nexport const unmock = (modulePath) => {\n mockStore.delete(modulePath);\n}\n\nexport const isMocked = (modulePath) => {\n return mockStore.has(modulePath);\n}\n","import {bold, green, red, yellow} from \"./consoleColor.js\";\n\nexport const getTestResultMsg = (title, success, fail) => {\n let msg = '\\n';\n\n msg += title;\n msg += green(success + ' passed') + ', ';\n if (fail) {\n msg += red(fail + ' failed') + ', ';\n }\n msg += bold(success + fail + ' total');\n\n return msg;\n};\n\nexport const getFileCountString = (n) => {\n return `\\nFound ${green(n)} test file(s)`;\n};\n\nexport const getFilePath = (path) => {\n return `\\n${yellow(path)}\\n`;\n};\n\nexport const getErrorMsgInLogic = (error) => {\n return red(`\\n✗ Test execution failed\\n Error: ${error}\\n`)\n};\n","import {formatFailureMessage, formatSuccessMessage, getMatcherForReplace, placeHolder} from \"./utils/formatString.js\";\nimport {clearAllMocks} from \"./mock/store.js\";\nimport {NUM, RESULT_MSG} from \"./constants/index.js\";\nimport {getTestResultMsg} from \"./utils/messages.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(RESULT_MSG.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 async run() {\n let passed = NUM.ZERO;\n let failed = NUM.ZERO;\n\n for (const test of testManager.getTests()) {\n try {\n await test.fn();\n console.log(formatSuccessMessage(test));\n passed++;\n clearAllMocks();\n } catch (error) {\n console.log(formatFailureMessage(test, error));\n failed++;\n }\n }\n\n console.log(getTestResultMsg(RESULT_MSG.TESTS, passed, failed));\n\n testManager.clearTests();\n\n return {passed, failed};\n }\n\n #formatDescription(args, description) {\n let argIndex = 0;\n return description.replace(getMatcherForReplace(), (match, type) => {\n if (argIndex >= args.length) return match;\n\n const formatter = placeHolder[type];\n\n return formatter ? formatter(args[argIndex++]) : match;\n });\n }\n}\n\nexport const testManager = new TestManager();","import {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\n/**\n * 테스트 케이스를 정의합니다.\n * @param {string} description - 테스트 설명\n * @param {Function} fn - 테스트 함수\n *\n * @example\n * test('더하기 테스트', () => {\n * expect(1 + 2).toBe(3);\n * });\n */\nexport const test = (description, fn) => testManager.test(description, fn);\n\n/**\n * 배열 형태의 테스트 케이스를 반복 실행합니다.\n * @param {Array<Array>} cases - 테스트 케이스 배열\n * @returns {Function} 테스트 실행 함수\n *\n * @example\n * test.each([\n * [1, 2, 3],\n * [2, 3, 5],\n * ])('add(%s, %s) = %s', (a, b, expected) => {\n * expect(a + b).toBe(expected);\n * });\n */\ntest.each = (cases) => testManager.testEach(cases);\n\n/**\n * 테스트 그룹을 정의합니다. 중첩 가능합니다.\n * @param {string} suiteName - 그룹 이름\n * @param {Function} fn - 그룹 내부 테스트들을 정의하는 함수\n *\n * @example\n * describe('계산기', () => {\n * test('더하기', () => {\n * expect(1 + 1).toBe(2);\n * });\n * });\n */\nexport const describe = (suiteName, fn) => testManager.describe(suiteName, fn);\n\n/**\n * 각 테스트 실행 전에 실행될 함수를 등록합니다.\n * @param {Function} fn - 전처리 함수\n *\n * @example\n * describe('카운터 테스트', () => {\n * let counter;\n *\n * beforeEach(() => {\n * counter = 0;\n * });\n *\n * test('초기값은 0', () => {\n * expect(counter).toBe(0);\n * });\n * });\n */\nexport const beforeEach = (fn) => testManager.beforeEach(fn);\n\n/**\n * 등록된 모든 테스트를 실행합니다.\n * @returns {Promise<{passed: number, failed: number}>} 테스트 결과\n *\n * @example\n * const { passed, failed } = await run();\n * console.log(`${passed} passed, ${failed} failed`);\n */\nexport const run = () => testManager.run();\n\n/**\n * 값을 검증하는 matcher 함수들을 반환합니다.\n * @function\n * @param {*} actual - 검증할 값\n * @returns {Object} matcher 함수들\n *\n * @example\n * expect(1 + 1).toBe(2);\n * expect([1, 2, 3]).toEqual([1, 2, 3]);\n * expect(() => { throw new Error('error') }).toThrow('error');\n * expect(true).toBeTruthy();\n * expect(false).toBeFalsy();\n */\nexport {expect};\n\nexport {mock, clearAllMocks, unmock, isMocked, mockStore};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/constants/view.js","../src/utils/consoleColor.js","../src/constants/index.js","../src/utils/formatString.js","../src/mock/utils/mockFunctions.js","../src/mock/utils/changeModuleExports.js","../src/mock/store.js","../src/cli/utils/messages.js","../src/testManager.js","../src/matchers.js","../src/expect.js","../index.js"],"sourcesContent":["export 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","import {COLORS} from \"../constants/view.js\";\n\nexport const colorize = (text, color) => `${color}${text}${COLORS.reset}`;\n\nexport const green = (text) => colorize(text, COLORS.green + COLORS.bold);\nexport const red = (text) => colorize(text, COLORS.red + COLORS.bold);\nexport const bold = (text) => colorize(text, COLORS.bold);\nexport const yellow = (text) => colorize(text, COLORS.yellow + COLORS.bold);\n","export 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 DANNYSIR_JS_TE: '@dannysir/js-te',\n};\n\nexport const RESULT_MSG = {\n TESTS: 'Tests: ',\n TOTAL: 'Total Result: ',\n CHECK: '✓ ',\n CROSS: '✗ ',\n DIRECTORY_DELIMITER: ' > ',\n EMPTY: '',\n};\n\nexport const NUM = {\n ZERO: 0,\n ONE: 1,\n};\n\nexport const MODULE_TYPE = {\n MODULE: 'module',\n ESM: 'esm',\n CJS: 'cjs',\n};\n","import {green, red} from \"./consoleColor.js\";\nimport {RESULT_MSG} from \"../constants/index.js\";\n\nexport const formatSuccessMessage = (test) => {\n const pathString = test.path === '' ? RESULT_MSG.EMPTY : test.path + RESULT_MSG.DIRECTORY_DELIMITER;\n return green(RESULT_MSG.CHECK) + pathString + test.description;\n};\n\nexport const formatFailureMessage = (test, error) => {\n const messages = [];\n messages.push(red(RESULT_MSG.CROSS) + test.path + test.description);\n messages.push(red(` Error Message : ${error.message}`));\n return messages.join('\\n');\n};\n\nexport const formatErrorMsg = (expect, actual) => {\n return `Expected ${JSON.stringify(expect)} but got ${JSON.stringify(actual)}`;\n};\n\nexport const formatThrowErrorMsg = (expect) => {\n return `Expected function to throw an error containing \"${expect}\", but it did not throw`;\n};\n","/**\n * 목업 함수의 메서드 타입 정의\n * @typedef {Object} MockMethods\n * @property {function(function): MockMethods} mockImplementation - 구현 설정\n * @property {function(...*): MockMethods} mockReturnValueOnce - 일회성 반환값 설정\n * @property {function(*): MockMethods} mockReturnValue - 반환값 설정\n * @property {function(): void} mockClear - 목업 상태 초기화\n */\n\n/**\n * @param {function} mockFn\n * @param {Object} state\n * @returns {{mockImplementation(*): this, mockReturnValueOnce(...[*]): this, mockReturnValue(*): this, mockClear(): this}|*}\n */\nexport const mockFunctions = (mockFn, state) => {\n return {\n mockImplementation(newImpl) {\n state.curImplement = newImpl;\n return mockFn;\n },\n\n mockReturnValueOnce(...value) {\n state.returnQueue.push(...value);\n return mockFn;\n },\n\n mockReturnValue(value) {\n state.curImplement = () => value;\n return mockFn;\n },\n\n mockClear() {\n state.returnQueue = [];\n state.curImplement = () => null;\n return mockFn;\n },\n }\n};\n","import {mockFunctions} from \"./mockFunctions.js\";\n\n/**\n * 목업 함수로 변환하는 로직\n * @param {function} [implementation] - 목업 함수 로직 (선택, 기본값: () => null)\n * @returns {(function(...*): *) & MockMethods} 호출 가능하면서 목업 메서드를 가진 함수\n */\nexport const makeMockFnc = (implementation = (() => null)) => {\n const state = {\n returnQueue: [],\n curImplement: implementation,\n };\n\n const mockFn = (...args) => {\n if (state.returnQueue.length > 0) {\n return state.returnQueue.shift();\n }\n return state.curImplement(...args);\n };\n\n const methods = mockFunctions(mockFn, state);\n Object.assign(mockFn, methods);\n\n return mockFn;\n};\n\nexport const changeModuleExports = (moduleExports) => {\n const result = {};\n\n for (const moduleName in moduleExports) {\n result[moduleName] = makeMockFnc(moduleExports[moduleName]);\n }\n\n return result;\n};\n","import {changeModuleExports} from \"./utils/changeModuleExports.js\";\n\nexport const mockStore = new Map();\n\nexport const clearAllMocks = () => {\n mockStore.clear();\n}\n\nexport const mock = (modulePath, mockExports) => {\n const mockedExports = changeModuleExports(mockExports);\n mockStore.set(modulePath, mockedExports);\n return mockStore.get(modulePath);\n}\n\nexport const unmock = (modulePath) => {\n mockStore.delete(modulePath);\n}\n\nexport const isMocked = (modulePath) => {\n return mockStore.has(modulePath);\n}\n","import {bold, green, red, yellow} from \"../../utils/consoleColor.js\";\n\nexport const getTestResultMsg = (title, success, fail) => {\n let msg = '\\n';\n\n msg += title;\n msg += green(success + ' passed') + ', ';\n if (fail) {\n msg += red(fail + ' failed') + ', ';\n }\n msg += bold(success + fail + ' total');\n\n return msg;\n};\n\nexport const getFileCountString = (n) => {\n return `\\nFound ${green(n)} test file(s)`;\n};\n\nexport const getFilePath = (path) => {\n return `\\n${yellow(path)}\\n`;\n};\n\nexport const getErrorMsgInLogic = (error) => {\n return red(`\\n✗ Test execution failed\\n Error: ${error}\\n`)\n};\n","import {formatFailureMessage, formatSuccessMessage} from \"./utils/formatString.js\";\nimport {clearAllMocks} from \"./mock/store.js\";\nimport {NUM, RESULT_MSG} from \"./constants/index.js\";\nimport {getTestResultMsg} from \"./cli/utils/messages.js\";\n\nclass TestManager {\n #tests = [];\n #testDepth = [];\n #beforeEachArr = [];\n #placeHolder = {\n 's': (value) => value,\n 'o': (value) => JSON.stringify(value),\n };\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(RESULT_MSG.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 async run() {\n let passed = NUM.ZERO;\n let failed = NUM.ZERO;\n\n for (const test of testManager.getTests()) {\n try {\n await test.fn();\n console.log(formatSuccessMessage(test));\n passed++;\n clearAllMocks();\n } catch (error) {\n console.log(formatFailureMessage(test, error));\n failed++;\n }\n }\n\n console.log(getTestResultMsg(RESULT_MSG.TESTS, passed, failed));\n\n testManager.clearTests();\n\n return {passed, failed};\n }\n\n #getMatcherForReplace = () => {\n return new RegExp(`%([${Object.keys(this.#placeHolder).join('')}])`, 'g')\n };\n\n #formatDescription(args, description) {\n let argIndex = 0;\n return description.replace(this.#getMatcherForReplace(), (match, type) => {\n if (argIndex >= args.length) return match;\n\n const formatter = this.#placeHolder[type];\n\n return formatter ? formatter(args[argIndex++]) : match;\n });\n }\n}\n\nexport const testManager = new TestManager();","import {formatErrorMsg, formatThrowErrorMsg} 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(formatErrorMsg(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(formatErrorMsg(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(formatErrorMsg(expected, e.message));\n }\n return;\n }\n throw new Error(formatThrowErrorMsg(expected));\n};\n\nexport const toBeTruthy = (actual) => {\n const value = runArgFnc(actual);\n if (!value) {\n throw new Error(formatErrorMsg(true, value));\n }\n};\n\nexport const toBeFalsy = (actual) => {\n const value = runArgFnc(actual);\n if (value) {\n throw new Error(formatErrorMsg(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\";\nimport {makeMockFnc} from \"./src/mock/utils/changeModuleExports.js\";\n\n/**\n * 테스트 케이스를 정의합니다.\n * @param {string} description - 테스트 설명\n * @param {Function} fn - 테스트 함수\n *\n * @example\n * test('더하기 테스트', () => {\n * expect(1 + 2).toBe(3);\n * });\n */\nexport const test = (description, fn) => testManager.test(description, fn);\n\n/**\n * 배열 형태의 테스트 케이스를 반복 실행합니다.\n * @param {Array<Array>} cases - 테스트 케이스 배열\n * @returns {Function} 테스트 실행 함수\n *\n * @example\n * test.each([\n * [1, 2, 3],\n * [2, 3, 5],\n * ])('add(%s, %s) = %s', (a, b, expected) => {\n * expect(a + b).toBe(expected);\n * });\n */\ntest.each = (cases) => testManager.testEach(cases);\n\n/**\n * 테스트 그룹을 정의합니다. 중첩 가능합니다.\n * @param {string} suiteName - 그룹 이름\n * @param {Function} fn - 그룹 내부 테스트들을 정의하는 함수\n *\n * @example\n * describe('계산기', () => {\n * test('더하기', () => {\n * expect(1 + 1).toBe(2);\n * });\n * });\n */\nexport const describe = (suiteName, fn) => testManager.describe(suiteName, fn);\n\n/**\n * 각 테스트 실행 전에 실행될 함수를 등록합니다.\n * @param {Function} fn - 전처리 함수\n *\n * @example\n * describe('카운터 테스트', () => {\n * let counter;\n *\n * beforeEach(() => {\n * counter = 0;\n * });\n *\n * test('초기값은 0', () => {\n * expect(counter).toBe(0);\n * });\n * });\n */\nexport const beforeEach = (fn) => testManager.beforeEach(fn);\n\n/**\n * 등록된 모든 테스트를 실행합니다.\n * @returns {Promise<{passed: number, failed: number}>} 테스트 결과\n *\n * @example\n * const { passed, failed } = await run();\n * console.log(`${passed} passed, ${failed} failed`);\n */\nexport const run = () => testManager.run();\n\n/**\n * 값을 검증하는 matcher 함수들을 반환합니다.\n * @function\n * @param {*} actual - 검증할 값\n * @returns {Object} matcher 함수들\n *\n * @example\n * expect(1 + 1).toBe(2);\n * expect([1, 2, 3]).toEqual([1, 2, 3]);\n * expect(() => { throw new Error('error') }).toThrow('error');\n * expect(true).toBeTruthy();\n * expect(false).toBeFalsy();\n */\nexport {expect};\n\nexport {mock, clearAllMocks, unmock, isMocked, mockStore};\n\nexport const fn = makeMockFnc;\n"],"names":[],"mappings":"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"}
package/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import {testManager} from "./src/testManager.js";
2
2
  import {clearAllMocks, isMocked, mock, unmock, mockStore} from './src/mock/store.js';
3
3
  import {expect} from "./src/expect.js";
4
+ import {makeMockFnc} from "./src/mock/utils/changeModuleExports.js";
4
5
 
5
6
  /**
6
7
  * 테스트 케이스를 정의합니다.
@@ -88,3 +89,5 @@ export const run = () => testManager.run();
88
89
  export {expect};
89
90
 
90
91
  export {mock, clearAllMocks, unmock, isMocked, mockStore};
92
+
93
+ export const fn = makeMockFnc;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dannysir/js-te",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "JavaScript test library",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -1,6 +1,6 @@
1
1
  import path from 'path';
2
- import {getFilePath} from "../utils/messages.js";
3
- import {transformFiles} from "../utils/transformFiles.js";
2
+ import {getFilePath} from "./utils/messages.js";
3
+ import {transformFiles} from "./utils/transformFiles.js";
4
4
  import {NUM} from "../constants/index.js";
5
5
 
6
6
  export const runTests = async (jsTe, mockedPaths, testFiles) => {
@@ -1,6 +1,6 @@
1
1
  import path from 'path';
2
2
  import fs from 'fs';
3
- import {MODULE_TYPE, NUM, PATH} from "../constants/index.js";
3
+ import {MODULE_TYPE, PATH} from "../constants/index.js";
4
4
 
5
5
  const getUserModuleType = () => {
6
6
  try {
@@ -1,7 +1,6 @@
1
- import {findAllSourceFiles, findTestFiles} from "../utils/findFiles.js";
2
- import {getFileCountString} from "../utils/messages.js";
3
- import {collectMockedPaths} from "../mock/collectMocks.js";
4
- import {transformFiles} from "../utils/transformFiles.js";
1
+ import {findAllSourceFiles, findTestFiles} from "./utils/findFiles.js";
2
+ import {collectMockedPaths} from "./utils/collectMocks.js";
3
+ import {transformFiles} from "./utils/transformFiles.js";
5
4
 
6
5
  export const setupFiles = () => {
7
6
  const testFiles = findTestFiles(process.cwd());
@@ -1,7 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import {transformSync} from "@babel/core";
3
- import {createMockCollectorPlugin} from "../babelPlugins/babelCollectMocks.js";
4
- import {BABEL} from "../constants/babel.js";
3
+ import {createMockCollectorPlugin} from "../../babelPlugins/babelCollectMocks.js";
4
+ import {BABEL} from "../../constants/babel.js";
5
5
 
6
6
  export const collectMockedPaths = (testFiles) => {
7
7
  const mockedPaths = new Set();
@@ -2,7 +2,7 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
 
4
4
 
5
- import {PATH} from "../constants/index.js";
5
+ import {PATH} from "../../constants/index.js";
6
6
 
7
7
  /**
8
8
  * 테스트 파일을 찾는 로직입니다.
@@ -1,4 +1,4 @@
1
- import {bold, green, red, yellow} from "./consoleColor.js";
1
+ import {bold, green, red, yellow} from "../../utils/consoleColor.js";
2
2
 
3
3
  export const getTestResultMsg = (title, success, fail) => {
4
4
  let msg = '\n';
@@ -1,8 +1,8 @@
1
1
  import fs from 'fs';
2
2
  import {transformSync} from "@babel/core";
3
- import {babelTransformImport} from "../babelPlugins/babelTransformImport.js";
4
- import {red} from "./consoleColor.js";
5
- import {BABEL} from "../constants/babel.js";
3
+ import {babelTransformImport} from "../../babelPlugins/babelTransformImport.js";
4
+ import {red} from "../../utils/consoleColor.js";
5
+ import {BABEL} from "../../constants/babel.js";
6
6
 
7
7
  const originalFiles = new Map();
8
8
 
package/src/matchers.js CHANGED
@@ -1,4 +1,4 @@
1
- import {getErrorMsg, getThrowErrorMsg} from "./utils/formatString.js";
1
+ import {formatErrorMsg, formatThrowErrorMsg} from "./utils/formatString.js";
2
2
 
3
3
  const runArgFnc = (actual) => {
4
4
  if (typeof actual === 'function') {
@@ -10,14 +10,14 @@ const runArgFnc = (actual) => {
10
10
  export const toBe = (actual, expected) => {
11
11
  const value = runArgFnc(actual);
12
12
  if (value !== expected) {
13
- throw new Error(getErrorMsg(expected, value));
13
+ throw new Error(formatErrorMsg(expected, value));
14
14
  }
15
15
  };
16
16
 
17
17
  export const toEqual = (actual, expected) => {
18
18
  const value = runArgFnc(actual);
19
19
  if (JSON.stringify(value) !== JSON.stringify(expected)) {
20
- throw new Error(getErrorMsg(expected, value));
20
+ throw new Error(formatErrorMsg(expected, value));
21
21
  }
22
22
  };
23
23
 
@@ -26,23 +26,23 @@ export const toThrow = (actual, expected) => {
26
26
  runArgFnc(actual);
27
27
  } catch (e) {
28
28
  if (!e.message.includes(expected)) {
29
- throw new Error(getErrorMsg(expected, e.message));
29
+ throw new Error(formatErrorMsg(expected, e.message));
30
30
  }
31
31
  return;
32
32
  }
33
- throw new Error(getThrowErrorMsg(expected));
33
+ throw new Error(formatThrowErrorMsg(expected));
34
34
  };
35
35
 
36
36
  export const toBeTruthy = (actual) => {
37
37
  const value = runArgFnc(actual);
38
38
  if (!value) {
39
- throw new Error(getErrorMsg(true, value));
39
+ throw new Error(formatErrorMsg(true, value));
40
40
  }
41
41
  };
42
42
 
43
43
  export const toBeFalsy = (actual) => {
44
44
  const value = runArgFnc(actual);
45
45
  if (value) {
46
- throw new Error(getErrorMsg(false, value));
46
+ throw new Error(formatErrorMsg(false, value));
47
47
  }
48
48
  };
package/src/mock/store.js CHANGED
@@ -1,3 +1,5 @@
1
+ import {changeModuleExports} from "./utils/changeModuleExports.js";
2
+
1
3
  export const mockStore = new Map();
2
4
 
3
5
  export const clearAllMocks = () => {
@@ -5,7 +7,9 @@ export const clearAllMocks = () => {
5
7
  }
6
8
 
7
9
  export const mock = (modulePath, mockExports) => {
8
- mockStore.set(modulePath, mockExports);
10
+ const mockedExports = changeModuleExports(mockExports);
11
+ mockStore.set(modulePath, mockedExports);
12
+ return mockStore.get(modulePath);
9
13
  }
10
14
 
11
15
  export const unmock = (modulePath) => {
@@ -0,0 +1,35 @@
1
+ import {mockFunctions} from "./mockFunctions.js";
2
+
3
+ /**
4
+ * 목업 함수로 변환하는 로직
5
+ * @param {function} [implementation] - 목업 함수 로직 (선택, 기본값: () => null)
6
+ * @returns {(function(...*): *) & MockMethods} 호출 가능하면서 목업 메서드를 가진 함수
7
+ */
8
+ export const makeMockFnc = (implementation = (() => null)) => {
9
+ const state = {
10
+ returnQueue: [],
11
+ curImplement: implementation,
12
+ };
13
+
14
+ const mockFn = (...args) => {
15
+ if (state.returnQueue.length > 0) {
16
+ return state.returnQueue.shift();
17
+ }
18
+ return state.curImplement(...args);
19
+ };
20
+
21
+ const methods = mockFunctions(mockFn, state);
22
+ Object.assign(mockFn, methods);
23
+
24
+ return mockFn;
25
+ };
26
+
27
+ export const changeModuleExports = (moduleExports) => {
28
+ const result = {};
29
+
30
+ for (const moduleName in moduleExports) {
31
+ result[moduleName] = makeMockFnc(moduleExports[moduleName]);
32
+ }
33
+
34
+ return result;
35
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * 목업 함수의 메서드 타입 정의
3
+ * @typedef {Object} MockMethods
4
+ * @property {function(function): MockMethods} mockImplementation - 구현 설정
5
+ * @property {function(...*): MockMethods} mockReturnValueOnce - 일회성 반환값 설정
6
+ * @property {function(*): MockMethods} mockReturnValue - 반환값 설정
7
+ * @property {function(): void} mockClear - 목업 상태 초기화
8
+ */
9
+
10
+ /**
11
+ * @param {function} mockFn
12
+ * @param {Object} state
13
+ * @returns {{mockImplementation(*): this, mockReturnValueOnce(...[*]): this, mockReturnValue(*): this, mockClear(): this}|*}
14
+ */
15
+ export const mockFunctions = (mockFn, state) => {
16
+ return {
17
+ mockImplementation(newImpl) {
18
+ state.curImplement = newImpl;
19
+ return mockFn;
20
+ },
21
+
22
+ mockReturnValueOnce(...value) {
23
+ state.returnQueue.push(...value);
24
+ return mockFn;
25
+ },
26
+
27
+ mockReturnValue(value) {
28
+ state.curImplement = () => value;
29
+ return mockFn;
30
+ },
31
+
32
+ mockClear() {
33
+ state.returnQueue = [];
34
+ state.curImplement = () => null;
35
+ return mockFn;
36
+ },
37
+ }
38
+ };
@@ -1,12 +1,16 @@
1
- import {formatFailureMessage, formatSuccessMessage, getMatcherForReplace, placeHolder} from "./utils/formatString.js";
1
+ import {formatFailureMessage, formatSuccessMessage} from "./utils/formatString.js";
2
2
  import {clearAllMocks} from "./mock/store.js";
3
3
  import {NUM, RESULT_MSG} from "./constants/index.js";
4
- import {getTestResultMsg} from "./utils/messages.js";
4
+ import {getTestResultMsg} from "./cli/utils/messages.js";
5
5
 
6
6
  class TestManager {
7
7
  #tests = [];
8
8
  #testDepth = [];
9
9
  #beforeEachArr = [];
10
+ #placeHolder = {
11
+ 's': (value) => value,
12
+ 'o': (value) => JSON.stringify(value),
13
+ };
10
14
 
11
15
  describe(str, fn) {
12
16
  this.#testDepth.push(str);
@@ -78,12 +82,16 @@ class TestManager {
78
82
  return {passed, failed};
79
83
  }
80
84
 
85
+ #getMatcherForReplace = () => {
86
+ return new RegExp(`%([${Object.keys(this.#placeHolder).join('')}])`, 'g')
87
+ };
88
+
81
89
  #formatDescription(args, description) {
82
90
  let argIndex = 0;
83
- return description.replace(getMatcherForReplace(), (match, type) => {
91
+ return description.replace(this.#getMatcherForReplace(), (match, type) => {
84
92
  if (argIndex >= args.length) return match;
85
93
 
86
- const formatter = placeHolder[type];
94
+ const formatter = this.#placeHolder[type];
87
95
 
88
96
  return formatter ? formatter(args[argIndex++]) : match;
89
97
  });
@@ -13,19 +13,10 @@ export const formatFailureMessage = (test, error) => {
13
13
  return messages.join('\n');
14
14
  };
15
15
 
16
- export const getErrorMsg = (expect, actual) => {
16
+ export const formatErrorMsg = (expect, actual) => {
17
17
  return `Expected ${JSON.stringify(expect)} but got ${JSON.stringify(actual)}`;
18
18
  };
19
19
 
20
- export const getThrowErrorMsg = (expect) => {
20
+ export const formatThrowErrorMsg = (expect) => {
21
21
  return `Expected function to throw an error containing "${expect}", but it did not throw`;
22
22
  };
23
-
24
- export const placeHolder = {
25
- 's': (value) => value,
26
- 'o': (value) => JSON.stringify(value),
27
- };
28
-
29
- export const getMatcherForReplace = () => {
30
- return new RegExp(`%([${Object.keys(placeHolder).join('')}])`, 'g')
31
- };