@dannysir/js-te 0.3.3 → 0.4.1
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 +260 -33
- package/dist/index.cjs +79 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +79 -2
- package/dist/index.mjs.map +1 -1
- package/index.js +3 -0
- package/package.json +1 -1
- package/src/babelPlugins/babelCollectMocks.js +2 -10
- package/src/babelPlugins/{babelTransformImport.js → babelTransform.js} +20 -3
- package/src/cli/setupFiles.js +1 -1
- package/src/cli/utils/transformFiles.js +2 -2
- package/src/mock/store.js +5 -1
- package/src/mock/utils/changeModuleExports.js +35 -0
- package/src/mock/utils/mockFunctions.js +38 -0
package/README.md
CHANGED
|
@@ -3,26 +3,32 @@
|
|
|
3
3
|
Jest에서 영감을 받아 만든 가벼운 JavaScript 테스트 프레임워크입니다.
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
## [📎 최근 업데이트 0.
|
|
7
|
-
|
|
8
|
-
### `mock`
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
6
|
+
## [📎 최근 업데이트 0.4.1v](https://github.com/dannysir/js-te-package/blob/main/CHANGELOG.md)
|
|
7
|
+
|
|
8
|
+
### `mock(path, mockObj)` 함수 개선
|
|
9
|
+
- 기존 `path`를 절대 경로만 등록 가능한 부분에 절대 경로도 가능하게 수정
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
test('[mock module] - mocking random function (absolute path)', async () => {
|
|
13
|
+
mock('../test-helper/random.js', { // 0.4.1부터 절대 경로도 등록 가능
|
|
14
|
+
random: () => 3,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
expect(play()).toBe(30);
|
|
18
|
+
});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Mock Functions 기능 추가
|
|
22
|
+
- `fn()` 함수로 모킹 가능한 함수 생성
|
|
23
|
+
- Mock function 메서드 지원
|
|
24
|
+
- `mockImplementation()` - 함수 구현 로직 변경
|
|
25
|
+
- `mockReturnValue()` - 고정된 반환값 설정
|
|
26
|
+
- `mockReturnValueOnce()` - 일회성 반환값 설정 (여러 번 호출 가능)
|
|
27
|
+
- `mockClear()` - mock 상태 초기화
|
|
28
|
+
### Module Mocking 개선
|
|
29
|
+
- `mock()` 함수가 모듈의 모든 함수를 자동으로 mock function으로 변환
|
|
30
|
+
- 변환된 함수들도 `mockImplementation()`, `mockReturnValue()` 등 사용 가능
|
|
31
|
+
|
|
26
32
|
---
|
|
27
33
|
## 설치
|
|
28
34
|
|
|
@@ -36,7 +42,7 @@ npm install --save-dev @dannysir/js-te
|
|
|
36
42
|
|
|
37
43
|
`*.test.js` 파일을 만들면 자동으로 찾아서 실행합니다.
|
|
38
44
|
|
|
39
|
-
별도의 import문 없이 `describe`와 `test`, `expect` 로직이 사용 가능합니다.
|
|
45
|
+
별도의 import문 없이 `describe`와 `test`, `expect` 로직이 사용 가능합니다.
|
|
40
46
|
|
|
41
47
|
```javascript
|
|
42
48
|
// math.test.js
|
|
@@ -177,18 +183,189 @@ const {random1, random2} = _module;
|
|
|
177
183
|
3. 테스트 실행
|
|
178
184
|
4. 원본 파일 복구
|
|
179
185
|
|
|
180
|
-
###
|
|
186
|
+
### Mock Functions
|
|
187
|
+
|
|
188
|
+
**0.4.0 버전부터 Jest와 유사한 Mock Functions 기능을 제공합니다.**
|
|
189
|
+
|
|
190
|
+
`fn()` 함수로 생성한 mock function은 반환값 제어 등의 기능을 제공합니다.
|
|
191
|
+
|
|
192
|
+
### `fn(implementation : optional)`
|
|
193
|
+
|
|
194
|
+
모킹 가능한 함수를 생성합니다.
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
import { fn } from '@dannysir/js-te';
|
|
198
|
+
|
|
199
|
+
test('mock function 기본 사용', () => {
|
|
200
|
+
const mockFn = fn();
|
|
201
|
+
|
|
202
|
+
mockFn('test');
|
|
203
|
+
mockFn(1, 2, 3);
|
|
204
|
+
|
|
205
|
+
// mock 함수는 기본적으로 undefined 반환
|
|
206
|
+
expect(mockFn()).toBe(undefined);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test('초기 구현과 함께 생성', () => {
|
|
210
|
+
const mockFn = fn((x, y) => x + y);
|
|
211
|
+
|
|
212
|
+
expect(mockFn(1, 2)).toBe(3);
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### `mockImplementation(fn)`
|
|
217
|
+
|
|
218
|
+
Mock 함수의 구현을 변경합니다.
|
|
219
|
+
|
|
220
|
+
```javascript
|
|
221
|
+
test('구현 변경하기', () => {
|
|
222
|
+
const mockFn = fn();
|
|
223
|
+
|
|
224
|
+
mockFn.mockImplementation((x) => x * 2);
|
|
225
|
+
|
|
226
|
+
expect(mockFn(5)).toBe(10);
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### `mockReturnValue(value)`
|
|
231
|
+
|
|
232
|
+
Mock 함수가 항상 특정 값을 반환하도록 설정합니다.
|
|
181
233
|
|
|
182
|
-
|
|
234
|
+
```javascript
|
|
235
|
+
test('고정 반환값 설정', () => {
|
|
236
|
+
const mockFn = fn();
|
|
237
|
+
|
|
238
|
+
mockFn.mockReturnValue(42);
|
|
239
|
+
|
|
240
|
+
expect(mockFn()).toBe(42);
|
|
241
|
+
expect(mockFn()).toBe(42);
|
|
242
|
+
expect(mockFn()).toBe(42);
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### `mockReturnValueOnce(...values)`
|
|
183
247
|
|
|
184
|
-
|
|
248
|
+
Mock 함수가 지정된 값들을 순서대로 한 번씩 반환하도록 설정합니다.
|
|
185
249
|
|
|
186
|
-
|
|
187
|
-
|
|
250
|
+
```javascript
|
|
251
|
+
test('일회성 반환값 설정', () => {
|
|
252
|
+
const mockFn = fn();
|
|
253
|
+
|
|
254
|
+
mockFn.mockReturnValueOnce(1, 2, 3);
|
|
255
|
+
|
|
256
|
+
expect(mockFn()).toBe(1);
|
|
257
|
+
expect(mockFn()).toBe(2);
|
|
258
|
+
expect(mockFn()).toBe(3);
|
|
259
|
+
expect(mockFn()).toBe(undefined); // 큐가 비면 기본값 반환
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test('mockReturnValueOnce와 mockReturnValue 조합', () => {
|
|
263
|
+
const mockFn = fn();
|
|
264
|
+
|
|
265
|
+
mockFn
|
|
266
|
+
.mockReturnValueOnce(1, 2)
|
|
267
|
+
.mockReturnValue(99);
|
|
268
|
+
|
|
269
|
+
expect(mockFn()).toBe(1);
|
|
270
|
+
expect(mockFn()).toBe(2);
|
|
271
|
+
expect(mockFn()).toBe(99); // 이후 계속 99 반환
|
|
272
|
+
expect(mockFn()).toBe(99);
|
|
273
|
+
});
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### `mockClear()`
|
|
277
|
+
|
|
278
|
+
Mock 함수의 상태를 초기화합니다.
|
|
279
|
+
|
|
280
|
+
```javascript
|
|
281
|
+
test('mock 상태 초기화', () => {
|
|
282
|
+
const mockFn = fn();
|
|
283
|
+
|
|
284
|
+
mockFn.mockReturnValue(42);
|
|
285
|
+
expect(mockFn()).toBe(42);
|
|
286
|
+
|
|
287
|
+
mockFn.mockClear();
|
|
288
|
+
expect(mockFn()).toBe(undefined); // 기본값으로 돌아감
|
|
289
|
+
});
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Module Mocking
|
|
293
|
+
|
|
294
|
+
#### `mock(모듈 경로), mock객체)`
|
|
295
|
+
|
|
296
|
+
모듈을 모킹합니다. ~~import 하기 **전에** 호출해야 합니다.~~
|
|
297
|
+
|
|
298
|
+
> **0.4.0 버전부터 `mock()` 함수가 모듈의 모든 함수를 자동으로 mock function으로 변환합니다.**
|
|
299
|
+
>
|
|
300
|
+
> **0.4.0 버전부터 `mock()` 함수가 모킹 객체를 리턴합니다.**
|
|
301
|
+
|
|
302
|
+
### **🚨 주의사항 (매우 중요)**
|
|
303
|
+
|
|
304
|
+
1. ~~**반드시 경로는 절대 경로로 입력해주세요.**~~
|
|
305
|
+
- ~~babel이 import문에서 절대 경로로 변환하여 확인을 하기 때문에 반드시 절대 경로로 등록해주세요.~~
|
|
306
|
+
> 0.4.1 버전부터 상대 경로 등록이 가능합니다.
|
|
188
307
|
2. ~~import문을 반드시 mocking 이후에 선언해주세요.~~
|
|
189
308
|
- ~~mocking 전에 import를 하게 되면 mocking되기 전의 모듈을 가져오게 됩니다.~~
|
|
190
309
|
|
|
191
310
|
> **0.3.0 버전부터 import문을 mock선언 이후에 하지 않아도 됩니다.**
|
|
311
|
+
3. **모킹한 모듈을 제어하고 싶다면 반드시 리턴 받은 객체를 활용하세요.**
|
|
312
|
+
|
|
313
|
+
**반환값 사용 예시**
|
|
314
|
+
```javascript
|
|
315
|
+
// math.js
|
|
316
|
+
export const add = (a, b) => a + b;
|
|
317
|
+
export const multiply = (a, b) => a * b;
|
|
318
|
+
|
|
319
|
+
// math.test.js
|
|
320
|
+
import { add, multiply} from './math.js';
|
|
321
|
+
test('mock 객체 반환값 활용', () => {
|
|
322
|
+
// mock() 함수가 모킹된 객체를 반환
|
|
323
|
+
// example - { add : 모킹함수, multiply : 모킹함수}
|
|
324
|
+
const mockedMath = mock('/absolute/path/to/math.js', {
|
|
325
|
+
add: (a, b) => a + b,
|
|
326
|
+
multiply: (a, b) => a * b
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// ⚠️ 중요: mock function 메서드는 반드시 반환받은 객체에 사용하세요
|
|
330
|
+
// 올바른 사용 ✅
|
|
331
|
+
mockedMath.add.mockReturnValue(100);
|
|
332
|
+
mockedMath.multiply.mockReturnValueOnce(50, 75);
|
|
333
|
+
// 잘못된 사용 ❌
|
|
334
|
+
// add.mockReturnValue(100); // 동작하지 않습니다!
|
|
335
|
+
|
|
336
|
+
expect(add(1, 2)).toBe(100);
|
|
337
|
+
expect(multiply(2, 3)).toBe(50);
|
|
338
|
+
expect(multiply(2, 3)).toBe(75);
|
|
339
|
+
|
|
340
|
+
});
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### **왜 반환된 객체를 사용해야 하나요?**
|
|
344
|
+
|
|
345
|
+
**간략 설명**
|
|
346
|
+
|
|
347
|
+
wrapper 패턴을 통해 모듈을 변경하기 때문에 mock function에 접근이 불가
|
|
348
|
+
|
|
349
|
+
**상세 설명**
|
|
350
|
+
|
|
351
|
+
`mock()` 함수는 모듈의 함수들을 mock function으로 변환하여 mockStore에 저장합니다.
|
|
352
|
+
|
|
353
|
+
하지만 `import`로 가져온 함수는 wrapper 함수이기 때문에 mock function의 메서드(`mockReturnValue`, `mockImplementation` 등)를 직접 가지고 있지 않습니다.
|
|
354
|
+
|
|
355
|
+
따라서 mock function의 메서드를 사용하려면 **반드시 `mock()` 함수가 반환한 객체**를 통해 접근해야 합니다.
|
|
356
|
+
```javascript
|
|
357
|
+
// 동작 원리
|
|
358
|
+
const mockedMath = mock('/path/to/math.js', {
|
|
359
|
+
add: (a, b) => a + b
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// mockedMath.add는 실제 mock function (메서드 있음) ✅
|
|
363
|
+
mockedMath.add.mockReturnValue(100);
|
|
364
|
+
|
|
365
|
+
// import로 가져온 add는 wrapper 함수 (메서드 없음) ❌
|
|
366
|
+
const { add } = await import('./math.js');
|
|
367
|
+
// add.mockReturnValue(100); // TypeError: add.mockReturnValue is not a function
|
|
368
|
+
```
|
|
192
369
|
|
|
193
370
|
**💡 부분 모킹(Partial Mocking)**
|
|
194
371
|
|
|
@@ -214,6 +391,8 @@ test('부분 모킹 예제', async () => {
|
|
|
214
391
|
});
|
|
215
392
|
```
|
|
216
393
|
|
|
394
|
+
> 모킹을 한 모듈에 mock function을 쓰고 싶으면
|
|
395
|
+
|
|
217
396
|
**📦 모듈 시스템 지원**
|
|
218
397
|
|
|
219
398
|
ESM(import)과 CommonJS(require) 모두 지원합니다.
|
|
@@ -242,19 +421,39 @@ import {play} from './game.js';
|
|
|
242
421
|
|
|
243
422
|
test('랜덤 함수 모킹', async () => {
|
|
244
423
|
// 1. 먼저 모킹
|
|
245
|
-
mock('/Users/san/Js-Te/test-helper/random.js', { //
|
|
424
|
+
mock('/Users/san/Js-Te/test-helper/random.js', { // 0.4.1 부터 상대 경로도 가능
|
|
246
425
|
random: () => 0.5
|
|
247
426
|
});
|
|
248
427
|
|
|
249
428
|
// 2. 모킹된 값 사용
|
|
250
429
|
expect(play()).toBe(5);
|
|
251
430
|
});
|
|
431
|
+
|
|
432
|
+
// 0.4.0 버전부터 mock functions 사용 가능
|
|
433
|
+
test('mock functions로 동적 제어', async () => {
|
|
434
|
+
const mocked = mock('/Users/san/Js-Te/test-helper/random.js', {
|
|
435
|
+
random: () => 0.5
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
expect(play()).toBe(5);
|
|
439
|
+
|
|
440
|
+
// 반환값 동적 변경
|
|
441
|
+
mocked.random.mockReturnValue(0.3);
|
|
442
|
+
expect(play()).toBe(3);
|
|
443
|
+
|
|
444
|
+
// 일회성 반환값 설정
|
|
445
|
+
mocked.random.mockReturnValueOnce(0.1);
|
|
446
|
+
expect(play()).toBe(1);
|
|
447
|
+
expect(play()).toBe(3); // 이전 설정값으로 복귀
|
|
448
|
+
});
|
|
252
449
|
```
|
|
253
450
|
|
|
254
451
|
### `clearAllMocks()`
|
|
255
452
|
|
|
256
453
|
등록된 모든 mock을 제거합니다.
|
|
257
454
|
|
|
455
|
+
**참고**: `clearAllMocks()`는 mockStore에서 mock을 제거하지만, 각 mock function의 내부 상태(returnQueue, implementation 등)는 초기화하지 않습니다. Mock function의 상태를 초기화하려면 각 함수의 `mockClear()` 메서드를 사용하세요.
|
|
456
|
+
|
|
258
457
|
### `unmock(모듈경로)`
|
|
259
458
|
|
|
260
459
|
특정 mock만 제거합니다.
|
|
@@ -374,20 +573,38 @@ describe('문자열 테스트', () => {
|
|
|
374
573
|
});
|
|
375
574
|
```
|
|
376
575
|
|
|
377
|
-
###
|
|
576
|
+
### Mock Functions 기본 사용
|
|
577
|
+
```javascript
|
|
578
|
+
import { fn } from '@dannysir/js-te';
|
|
378
579
|
|
|
379
|
-
|
|
580
|
+
test('콜백 함수 모킹', () => {
|
|
581
|
+
const mockCallback = fn((x) => x * 2);
|
|
582
|
+
|
|
583
|
+
expect(mockCallback(21)).toBe(42);
|
|
584
|
+
|
|
585
|
+
// 구현 변경
|
|
586
|
+
mockCallback.mockImplementation((x) => x + 10);
|
|
587
|
+
expect(mockCallback(5)).toBe(15);
|
|
588
|
+
});
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
### 전체 모킹
|
|
592
|
+
#### 모듈 모킹 (전체)
|
|
380
593
|
```javascript
|
|
381
594
|
// mocking.test.js
|
|
382
595
|
import {random} from '../src/test-helper/game.js'; // 0.2.4 버전부터 import문 상단 배치 가능
|
|
383
596
|
|
|
384
597
|
test('[mocking] - mocking random function', async () => {
|
|
385
|
-
mock('/Users/san/Js-Te/test-helper/random.js', {
|
|
598
|
+
const mocked = mock('/Users/san/Js-Te/test-helper/random.js', {
|
|
386
599
|
random: () => 3,
|
|
387
600
|
});
|
|
388
601
|
// 0.3.0 버전 이전까지는 반드시 mock 이후 동적 import문 작성
|
|
389
602
|
// const {play} = await import('../src/test-helper/game.js');
|
|
390
603
|
expect(play()).toBe(30);
|
|
604
|
+
|
|
605
|
+
// 0.4.0 버전부터: 동적으로 반환값 변경 가능
|
|
606
|
+
mocked.random.mockReturnValue(5);
|
|
607
|
+
expect(play()).toBe(50);
|
|
391
608
|
});
|
|
392
609
|
|
|
393
610
|
// game.js
|
|
@@ -401,7 +618,7 @@ export const play = () => {
|
|
|
401
618
|
export const random = () => Math.random();
|
|
402
619
|
```
|
|
403
620
|
|
|
404
|
-
####
|
|
621
|
+
#### 모듈 모킹 (부분)
|
|
405
622
|
```javascript
|
|
406
623
|
// calculator.js
|
|
407
624
|
export const add = (a, b) => a + b;
|
|
@@ -411,7 +628,7 @@ export const multiply = (a, b) => a * b;
|
|
|
411
628
|
// calculator.test.js
|
|
412
629
|
test('[partial mocking] - mock only multiply', async () => {
|
|
413
630
|
// multiply만 모킹, add와 subtract는 원본 사용
|
|
414
|
-
mock('/Users/san/Js-Te/calculator.js', {
|
|
631
|
+
const mocked = mock('/Users/san/Js-Te/calculator.js', {
|
|
415
632
|
multiply: (a, b) => 999
|
|
416
633
|
});
|
|
417
634
|
|
|
@@ -419,7 +636,17 @@ test('[partial mocking] - mock only multiply', async () => {
|
|
|
419
636
|
|
|
420
637
|
expect(add(2, 3)).toBe(5);
|
|
421
638
|
expect(subtract(5, 2)).toBe(3);
|
|
422
|
-
expect(multiply(2, 3)).toBe(999);
|
|
639
|
+
expect(multiply(2, 3)).toBe(999);
|
|
640
|
+
|
|
641
|
+
// 0.4.0 버전부터: mock function 메서드 사용 가능
|
|
642
|
+
mocked.multiply.mockReturnValue(100);
|
|
643
|
+
expect(multiply(2, 3)).toBe(100);
|
|
644
|
+
|
|
645
|
+
// 일회성 반환값
|
|
646
|
+
mocked.multiply.mockReturnValueOnce(50, 75);
|
|
647
|
+
expect(multiply(2, 3)).toBe(50);
|
|
648
|
+
expect(multiply(2, 3)).toBe(75);
|
|
649
|
+
expect(multiply(2, 3)).toBe(100); // 이전 설정값으로 복귀
|
|
423
650
|
});
|
|
424
651
|
```
|
|
425
652
|
|
package/dist/index.cjs
CHANGED
|
@@ -44,6 +44,79 @@ const formatThrowErrorMsg = (expect) => {
|
|
|
44
44
|
return `Expected function to throw an error containing "${expect}", but it did not throw`;
|
|
45
45
|
};
|
|
46
46
|
|
|
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
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
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;
|
|
118
|
+
};
|
|
119
|
+
|
|
47
120
|
const mockStore = new Map();
|
|
48
121
|
|
|
49
122
|
const clearAllMocks = () => {
|
|
@@ -51,7 +124,9 @@ const clearAllMocks = () => {
|
|
|
51
124
|
};
|
|
52
125
|
|
|
53
126
|
const mock = (modulePath, mockExports) => {
|
|
54
|
-
|
|
127
|
+
const mockedExports = changeModuleExports(mockExports);
|
|
128
|
+
mockStore.set(modulePath, mockedExports);
|
|
129
|
+
return mockStore.get(modulePath);
|
|
55
130
|
};
|
|
56
131
|
|
|
57
132
|
const unmock = (modulePath) => {
|
|
@@ -309,10 +384,13 @@ const beforeEach = (fn) => testManager.beforeEach(fn);
|
|
|
309
384
|
*/
|
|
310
385
|
const run = () => testManager.run();
|
|
311
386
|
|
|
387
|
+
const fn = makeMockFnc;
|
|
388
|
+
|
|
312
389
|
exports.beforeEach = beforeEach;
|
|
313
390
|
exports.clearAllMocks = clearAllMocks;
|
|
314
391
|
exports.describe = describe;
|
|
315
392
|
exports.expect = expect;
|
|
393
|
+
exports.fn = fn;
|
|
316
394
|
exports.isMocked = isMocked;
|
|
317
395
|
exports.mock = mock;
|
|
318
396
|
exports.mockStore = mockStore;
|
package/dist/index.cjs.map
CHANGED
|
@@ -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/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","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 \"../../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\";\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
|
@@ -42,6 +42,79 @@ const formatThrowErrorMsg = (expect) => {
|
|
|
42
42
|
return `Expected function to throw an error containing "${expect}", but it did not throw`;
|
|
43
43
|
};
|
|
44
44
|
|
|
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
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
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;
|
|
116
|
+
};
|
|
117
|
+
|
|
45
118
|
const mockStore = new Map();
|
|
46
119
|
|
|
47
120
|
const clearAllMocks = () => {
|
|
@@ -49,7 +122,9 @@ const clearAllMocks = () => {
|
|
|
49
122
|
};
|
|
50
123
|
|
|
51
124
|
const mock = (modulePath, mockExports) => {
|
|
52
|
-
|
|
125
|
+
const mockedExports = changeModuleExports(mockExports);
|
|
126
|
+
mockStore.set(modulePath, mockedExports);
|
|
127
|
+
return mockStore.get(modulePath);
|
|
53
128
|
};
|
|
54
129
|
|
|
55
130
|
const unmock = (modulePath) => {
|
|
@@ -307,5 +382,7 @@ const beforeEach = (fn) => testManager.beforeEach(fn);
|
|
|
307
382
|
*/
|
|
308
383
|
const run = () => testManager.run();
|
|
309
384
|
|
|
310
|
-
|
|
385
|
+
const fn = makeMockFnc;
|
|
386
|
+
|
|
387
|
+
export { beforeEach, clearAllMocks, describe, expect, fn, isMocked, mock, mockStore, run, test, unmock };
|
|
311
388
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -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/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","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 \"../../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\";\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,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import {BABEL} from "../constants/babel.js";
|
|
1
|
+
import {findAbsolutePath} from "./utils/pathHelper.js";
|
|
4
2
|
|
|
5
3
|
export const createMockCollectorPlugin = (mockedPaths) => {
|
|
6
4
|
return ({types: t}) => {
|
|
@@ -18,14 +16,8 @@ export const createMockCollectorPlugin = (mockedPaths) => {
|
|
|
18
16
|
|
|
19
17
|
const mockPath = args[0].value;
|
|
20
18
|
const currentFilePath = state.filename || process.cwd();
|
|
21
|
-
const currentDir = path.dirname(currentFilePath);
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
if (mockPath.startsWith(BABEL.PERIOD)) {
|
|
25
|
-
absolutePath = path.resolve(currentDir, mockPath);
|
|
26
|
-
} else {
|
|
27
|
-
absolutePath = mockPath;
|
|
28
|
-
}
|
|
20
|
+
const absolutePath = findAbsolutePath(mockPath, currentFilePath);
|
|
29
21
|
|
|
30
22
|
mockedPaths.add(absolutePath);
|
|
31
23
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
2
1
|
import {findAbsolutePath, shouldTransform} from "./utils/pathHelper.js";
|
|
3
2
|
import {getModuleInfo} from "./utils/getModuleInfo.js";
|
|
4
3
|
import {createNamespaceWrapper, createOriginalDeclaration, createWrapperFunction} from "./utils/wrapperCreator.js";
|
|
5
4
|
import {BABEL, MOCK} from "../constants/babel.js";
|
|
6
5
|
|
|
7
|
-
export const
|
|
6
|
+
export const babelTransform = (mockedPaths = null) => {
|
|
8
7
|
return ({types: t}) => {
|
|
9
8
|
return {
|
|
10
9
|
visitor: {
|
|
@@ -177,7 +176,25 @@ export const babelTransformImport = (mockedPaths = null) => {
|
|
|
177
176
|
nodePath.node.declarations = newDeclarations;
|
|
178
177
|
nodePath.node._transformed = true;
|
|
179
178
|
}
|
|
180
|
-
}
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
CallExpression(nodePath, state) {
|
|
182
|
+
if (!t.isIdentifier(nodePath.node.callee, { name: 'mock' })) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const args = nodePath.node.arguments;
|
|
187
|
+
if (args.length < 1 || !t.isStringLiteral(args[0])) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const mockPath = args[0].value;
|
|
192
|
+
const currentFilePath = state.filename || process.cwd();
|
|
193
|
+
|
|
194
|
+
const absolutePath = findAbsolutePath(mockPath, currentFilePath);
|
|
195
|
+
|
|
196
|
+
nodePath.node.arguments[0] = t.stringLiteral(absolutePath);
|
|
197
|
+
},
|
|
181
198
|
}
|
|
182
199
|
};
|
|
183
200
|
}
|
package/src/cli/setupFiles.js
CHANGED
|
@@ -4,8 +4,8 @@ import {transformFiles} from "./utils/transformFiles.js";
|
|
|
4
4
|
|
|
5
5
|
export const setupFiles = () => {
|
|
6
6
|
const testFiles = findTestFiles(process.cwd());
|
|
7
|
-
const mockedPaths = collectMockedPaths(testFiles);
|
|
8
7
|
const sourceFiles = findAllSourceFiles(process.cwd());
|
|
8
|
+
const mockedPaths = collectMockedPaths(testFiles);
|
|
9
9
|
|
|
10
10
|
for (const file of sourceFiles) {
|
|
11
11
|
transformFiles(file, mockedPaths);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import {transformSync} from "@babel/core";
|
|
3
|
-
import {
|
|
3
|
+
import {babelTransform} from "../../babelPlugins/babelTransform.js";
|
|
4
4
|
import {red} from "../../utils/consoleColor.js";
|
|
5
5
|
import {BABEL} from "../../constants/babel.js";
|
|
6
6
|
|
|
@@ -17,7 +17,7 @@ export const transformFiles = (filePath, mockPath) => {
|
|
|
17
17
|
|
|
18
18
|
const transformed = transformSync(originalCode, {
|
|
19
19
|
filename: filePath,
|
|
20
|
-
plugins: [
|
|
20
|
+
plugins: [babelTransform(mockPath)],
|
|
21
21
|
parserOpts: {
|
|
22
22
|
sourceType: BABEL.MODULE,
|
|
23
23
|
plugins: ['dynamicImport']
|
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
|
-
|
|
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
|
+
};
|