@dannysir/js-te 0.2.3 → 0.3.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 CHANGED
@@ -3,23 +3,19 @@
3
3
  Jest에서 영감을 받아 만든 가벼운 JavaScript 테스트 프레임워크입니다.
4
4
 
5
5
 
6
- ## [📎 최근 업데이트 0.2.3v](https://github.com/dannysir/js-te-package/blob/feature-import-hoisting/CHANGELOG.md)
7
-
8
-
9
- ### 문서 수정
10
- - README.md 내에 type 설정 관련 설명 수정
11
- - 0.2.1 버전부터 ESM 방식과 Common JS 방식 모두 허용
12
- - 개발 블로그 링크 추가
13
-
14
- ### 설정 수정
15
- - package.json
16
- - 레포지토리 및 이슈 링크 추가
17
-
18
- ### `rollup.config.js` output 파일명 수정
19
- - 기존 `.cjs.js`와 같은 이름에서 `.cjs`로 수정
20
- - `esm.js`에서 `.mjs`로 수정
6
+ ## [📎 최근 업데이트 0.3.0v](https://github.com/dannysir/js-te-package/blob/main/CHANGELOG.md)
21
7
 
22
8
 
9
+ ### `mock` 이후 import를 해야하는 문제 해결
10
+ - 문제 : 기존의 경우 모킹 기능 이용시 반드시 동적 import문을 mock 다음에 작성해야 했음
11
+ - 해결
12
+ - 기존 `mockStore`를 직접 비교하여 import하는 방식에서 wrapper 패턴을 이용하도록 적용
13
+ - 모듈을 새로운 함수로 만들어 함수를 실행할 때마다 `mockStore`와 비교하여 값을 리턴하도록 수정
14
+ ### 모듈 변환 최적화
15
+ - 문제 : 앞선 변경으로 인해 모든 파일의 모듈들이 사용될 때마다 `mockStore`와 비교하는 로직이 실행됨
16
+ - 해결
17
+ - `cli`로직에 mock을 미리 검사하여 mock 경로를 미리 저장하는 로직을 추가
18
+ - 미리 확인한 mock 경로를 이용해 import문이 만약 저장된 경로일 때만 babel 변환
23
19
 
24
20
  ---
25
21
  ## 설치
@@ -155,10 +151,23 @@ Babel을 사용해서 import/require 구문을 변환하여 mock 함수를 가
155
151
  1. `mock(path, mockObj)` 선언 확인
156
152
  2. `path`를 key로 이용해 Map에 저장
157
153
  2. Babel로 코드 변환
158
- 1. 전체 파일의 import/require문 확인
159
- 2. (0.0.3 버전 추가) import 경로를 **절대 경로**로 변환
160
- 2. import 경로(절대 경로)가 Map에 존재하면 mock 객체로 변환
161
- 3. import 경로(절대 경로) Map에 없다면 그대로 import
154
+ ```jsx
155
+ // 0.3.0 버전 이후
156
+ const _original = await import('./random.js');
157
+ const random = (...args) => {
158
+ const module = mockStore.has('/path/to/random.js')
159
+ ? { ..._original, ...mockStore.get('/path/to/random.js') }
160
+ : _original;
161
+ return module.random(...args);
162
+ };
163
+
164
+ // 0.3.0 버전 이전
165
+ const _original = await import('./random.js');
166
+ const _module = mockStore.has('/path/to/random.js')
167
+ ? { ..._original, ...mockStore.get('/path/to/random.js') }
168
+ : _original;
169
+ const {random1, random2} = _module;
170
+ ```
162
171
  3. 테스트 실행
163
172
  4. 원본 파일 복구
164
173
 
@@ -170,8 +179,10 @@ Babel을 사용해서 import/require 구문을 변환하여 mock 함수를 가
170
179
 
171
180
  1. 반드시 경로는 절대 경로로 입력해주세요.
172
181
  - babel이 import문에서 절대 경로로 변환하여 확인을 하기 때문에 반드시 절대 경로로 등록해주세요.
173
- 2. import문을 반드시 mocking 이후에 선언해주세요.
174
- - mocking 전에 import를 하게 되면 mocking되기 전의 모듈을 가져오게 됩니다.
182
+ 2. ~~import문을 반드시 mocking 이후에 선언해주세요.~~
183
+ - ~~mocking 전에 import를 하게 되면 mocking되기 전의 모듈을 가져오게 됩니다.~~
184
+
185
+ > **0.3.0 버전부터 import문을 mock선언 이후에 하지 않아도 됩니다.**
175
186
 
176
187
  **💡 부분 모킹(Partial Mocking)**
177
188
 
@@ -184,14 +195,13 @@ export const subtract = (a, b) => a - b;
184
195
  export const multiply = (a, b) => a * b;
185
196
 
186
197
  // math.test.js
198
+ const { add, multiply } = import('./math.js'); // 0.3.0 버전부터는 최상단에 선언 가능
199
+
187
200
  test('부분 모킹 예제', async () => {
188
- // multiply만 모킹하고 add, subtract는 원본 사용
189
- mock('/Users/san/Js-Te/math.js', {
190
- multiply: () => 100 // multiply만 모킹
201
+ mock('/Users/san/untitled/index.js', {
202
+ multiply: () => 100
191
203
  });
192
204
 
193
- const { add, subtract, multiply } = await import('./math.js');
194
-
195
205
  expect(add(2, 3)).toBe(5); // 원본 함수 사용
196
206
  expect(subtract(5, 3)).toBe(2); // 원본 함수 사용
197
207
  expect(multiply(2, 3)).toBe(100); // 모킹된 함수 사용
@@ -222,17 +232,15 @@ import { random } from './random.js'; // 자유롭게 import하면 babel에서
222
232
  export const play = () => random() * 10;
223
233
 
224
234
  // game.test.js
235
+ import {play} from './game.js';
236
+
225
237
  test('랜덤 함수 모킹', async () => {
226
238
  // 1. 먼저 모킹
227
239
  mock('/Users/san/Js-Te/test-helper/random.js', { // 반드시 절대 경로로 등록
228
240
  random: () => 0.5
229
241
  });
230
242
 
231
- // 2. 다음 import (CommonJS도 가능)
232
- const { play } = await import('./game.js');
233
- // 또는: const { play } = require('./game.js');
234
-
235
- // 3. 모킹된 값 사용
243
+ // 2. 모킹된 사용
236
244
  expect(play()).toBe(5);
237
245
  });
238
246
  ```
@@ -365,11 +373,14 @@ describe('문자열 테스트', () => {
365
373
  #### 전체 모킹
366
374
  ```javascript
367
375
  // mocking.test.js
376
+ import {random} from '../src/test-helper/game.js'; // 0.2.4 버전부터 import문 상단 배치 가능
377
+
368
378
  test('[mocking] - mocking random function', async () => {
369
379
  mock('/Users/san/Js-Te/test-helper/random.js', {
370
380
  random: () => 3,
371
381
  });
372
- const {play} = await import('../src/test-helper/game.js');
382
+ // 0.3.0 버전 이전까지는 반드시 mock 이후 동적 import문 작성
383
+ // const {play} = await import('../src/test-helper/game.js');
373
384
  expect(play()).toBe(30);
374
385
  });
375
386
 
@@ -413,7 +424,7 @@ test('[partial mocking] - mock only multiply', async () => {
413
424
 
414
425
  ## 만든 이유
415
426
 
416
- 우아한테크코스 과제하면서 Jest 써보고 테스트 프레임워크가 어떻게 동작하는지 궁금해서 만들어봤습니다.
427
+ Jest를 사용하며 JavaScript 테스트 라이브러리의 구조가 궁금하여 만들게 되었습니다.
417
428
 
418
429
  ## 라이선스
419
430
 
@@ -0,0 +1,34 @@
1
+ import path from 'path';
2
+ import { BABEL } from "./constants.js";
3
+
4
+ export const createMockCollectorPlugin = (mockedPaths) => {
5
+ return ({types: t}) => {
6
+ return {
7
+ visitor: {
8
+ CallExpression(nodePath, state) {
9
+ if (!t.isIdentifier(nodePath.node.callee, {name: 'mock'})) {
10
+ return;
11
+ }
12
+
13
+ const args = nodePath.node.arguments;
14
+ if (args.length < 1 || !t.isStringLiteral(args[0])) {
15
+ return;
16
+ }
17
+
18
+ const mockPath = args[0].value;
19
+ const currentFilePath = state.filename || process.cwd();
20
+ const currentDir = path.dirname(currentFilePath);
21
+
22
+ let absolutePath;
23
+ if (mockPath.startsWith(BABEL.PERIOD)) {
24
+ absolutePath = path.resolve(currentDir, mockPath);
25
+ } else {
26
+ absolutePath = mockPath;
27
+ }
28
+
29
+ mockedPaths.add(absolutePath);
30
+ }
31
+ }
32
+ };
33
+ };
34
+ };
@@ -1,214 +1,317 @@
1
1
  import path from 'path';
2
2
  import {BABEL, MOCK} from "./constants.js";
3
3
 
4
- export const babelTransformImport = ({types: t}) => {
5
- return {
6
- visitor: {
7
- Program(path) {
8
- const mockStoreDeclaration = t.VariableDeclaration('const', [
9
- t.VariableDeclarator(
10
- t.Identifier(MOCK.STORE_NAME),
11
- t.MemberExpression(
12
- t.Identifier('global'),
13
- t.Identifier(MOCK.STORE_NAME)
4
+ export const babelTransformImport = (mockedPaths = null) => {
5
+ return ({types: t}) => {
6
+ return {
7
+ visitor: {
8
+ Program(path) {
9
+ const mockStoreDeclaration = t.VariableDeclaration('const', [
10
+ t.VariableDeclarator(
11
+ t.Identifier(MOCK.STORE_NAME),
12
+ t.MemberExpression(
13
+ t.Identifier('global'),
14
+ t.Identifier(MOCK.STORE_NAME)
15
+ )
14
16
  )
15
- )
16
- ]);
17
- path.node.body.unshift(mockStoreDeclaration);
18
- },
17
+ ]);
18
+ path.node.body.unshift(mockStoreDeclaration);
19
+ },
19
20
 
20
- ImportDeclaration(nodePath, state) {
21
- const source = nodePath.node.source.value;
21
+ ImportDeclaration(nodePath, state) {
22
+ const source = nodePath.node.source.value;
22
23
 
23
- if (source === MOCK.STORE_PATH) {
24
- return;
25
- }
24
+ if (source === MOCK.STORE_PATH) {
25
+ return;
26
+ }
26
27
 
27
- const currentFilePath = state.filename || process.cwd();
28
- const currentDir = path.dirname(currentFilePath);
28
+ const currentFilePath = state.filename || process.cwd();
29
+ const currentDir = path.dirname(currentFilePath);
29
30
 
30
- let absolutePath;
31
- if (source.startsWith(BABEL.PERIOD)) {
32
- absolutePath = path.resolve(currentDir, source);
33
- } else {
34
- absolutePath = source;
35
- }
31
+ let absolutePath;
32
+ if (source.startsWith(BABEL.PERIOD)) {
33
+ absolutePath = path.resolve(currentDir, source);
34
+ } else {
35
+ absolutePath = source;
36
+ }
36
37
 
37
- const specifiers = nodePath.node.specifiers;
38
+ if (mockedPaths && !mockedPaths.has(absolutePath)) {
39
+ return;
40
+ }
38
41
 
39
- const originalVarName = nodePath.scope.generateUidIdentifier('original');
40
- const moduleVarName = nodePath.scope.generateUidIdentifier(BABEL.MODULE);
42
+ const specifiers = nodePath.node.specifiers;
43
+ const originalVarName = nodePath.scope.generateUidIdentifier('original');
41
44
 
42
- /*
43
- const _original = await import('./random.js');
44
- const _module = mockStore.has('/path/to/random.js')
45
- ? { ..._original, ...mockStore.get('/path/to/random.js') }
46
- : _original;
47
- const {random1, random2} = _module;
48
- */
45
+ /*
49
46
 
50
- const originalDeclaration = t.variableDeclaration(BABEL.CONST, [
51
- t.variableDeclarator(
52
- originalVarName,
53
- t.awaitExpression(
54
- t.importExpression(t.stringLiteral(source))
47
+ const _original = await import('./random.js');
48
+ const random = (...args) => {
49
+ const module = mockStore.has('/path/to/random.js')
50
+ ? { ..._original, ...mockStore.get('/path/to/random.js') }
51
+ : _original;
52
+ return module.random(...args);
53
+ };
54
+ */
55
+
56
+ const originalDeclaration = t.variableDeclaration(BABEL.CONST, [
57
+ t.variableDeclarator(
58
+ originalVarName,
59
+ t.awaitExpression(
60
+ t.importExpression(t.stringLiteral(source))
61
+ )
55
62
  )
56
- )
57
- ]);
58
-
59
- const moduleDeclaration = t.variableDeclaration(BABEL.CONST, [
60
- t.variableDeclarator(
61
- moduleVarName,
62
- t.conditionalExpression(
63
- t.callExpression(
64
- t.memberExpression(t.identifier(MOCK.STORE_NAME), t.identifier(BABEL.HAS)),
65
- [t.stringLiteral(absolutePath)]
66
- ),
67
- t.objectExpression([
68
- t.spreadElement(originalVarName),
69
- t.spreadElement(
63
+ ]);
64
+
65
+ const wrapperDeclarations = specifiers.map(spec => {
66
+ let importedName, localName;
67
+
68
+ if (t.isImportDefaultSpecifier(spec)) {
69
+ importedName = 'default';
70
+ localName = spec.local.name;
71
+ } else if (t.isImportNamespaceSpecifier(spec)) {
72
+ localName = spec.local.name;
73
+ return t.variableDeclarator(
74
+ t.identifier(localName),
75
+ t.conditionalExpression(
70
76
  t.callExpression(
71
- t.memberExpression(t.identifier(MOCK.STORE_NAME), t.identifier(BABEL.GET)),
77
+ t.memberExpression(t.identifier(MOCK.STORE_NAME), t.identifier(BABEL.HAS)),
72
78
  [t.stringLiteral(absolutePath)]
73
- )
79
+ ),
80
+ t.objectExpression([
81
+ t.spreadElement(originalVarName),
82
+ t.spreadElement(
83
+ t.callExpression(
84
+ t.memberExpression(t.identifier(MOCK.STORE_NAME), t.identifier(BABEL.GET)),
85
+ [t.stringLiteral(absolutePath)]
86
+ )
87
+ )
88
+ ]),
89
+ originalVarName
74
90
  )
75
- ]),
76
- originalVarName
77
- )
78
- )
79
- ]);
91
+ );
92
+ } else {
93
+ importedName = spec.imported.name;
94
+ localName = spec.local.name;
95
+ }
80
96
 
81
- const extractDeclarations = specifiers.map(spec => {
82
- let importedName, localName;
83
-
84
- if (t.isImportDefaultSpecifier(spec)) {
85
- importedName = 'default';
86
- localName = spec.local.name;
87
- } else if (t.isImportNamespaceSpecifier(spec)) {
88
- localName = spec.local.name;
89
97
  return t.variableDeclarator(
90
98
  t.identifier(localName),
91
- moduleVarName
99
+ t.arrowFunctionExpression(
100
+ [t.restElement(t.identifier('args'))],
101
+ t.blockStatement([
102
+ t.variableDeclaration(BABEL.CONST, [
103
+ t.variableDeclarator(
104
+ t.identifier(BABEL.MODULE),
105
+ t.conditionalExpression(
106
+ t.callExpression(
107
+ t.memberExpression(t.identifier(MOCK.STORE_NAME), t.identifier(BABEL.HAS)),
108
+ [t.stringLiteral(absolutePath)]
109
+ ),
110
+ t.objectExpression([
111
+ t.spreadElement(originalVarName),
112
+ t.spreadElement(
113
+ t.callExpression(
114
+ t.memberExpression(t.identifier(MOCK.STORE_NAME), t.identifier(BABEL.GET)),
115
+ [t.stringLiteral(absolutePath)]
116
+ )
117
+ )
118
+ ]),
119
+ originalVarName
120
+ )
121
+ )
122
+ ]),
123
+ t.returnStatement(
124
+ t.callExpression(
125
+ t.memberExpression(t.identifier(BABEL.MODULE), t.identifier(importedName)),
126
+ [t.spreadElement(t.identifier('args'))]
127
+ )
128
+ )
129
+ ])
130
+ )
92
131
  );
93
- } else {
94
- importedName = spec.imported.name;
95
- localName = spec.local.name;
96
- }
132
+ });
97
133
 
98
- return t.variableDeclarator(
99
- t.identifier(localName),
100
- t.memberExpression(moduleVarName, t.identifier(importedName))
101
- );
102
- });
134
+ const wrapperDeclaration = t.variableDeclaration(BABEL.CONST, wrapperDeclarations);
103
135
 
104
- const extractDeclaration = t.variableDeclaration(BABEL.CONST, extractDeclarations);
105
-
106
- nodePath.replaceWithMultiple([originalDeclaration, moduleDeclaration, extractDeclaration]);
107
- },
108
-
109
- VariableDeclaration(nodePath, state) {
110
- const declarations = nodePath.node.declarations;
111
-
112
- if (!declarations || declarations.length === 0) {
113
- return;
114
- }
115
-
116
- if (nodePath.node._transformed) {
117
- return;
118
- }
136
+ nodePath.replaceWithMultiple([originalDeclaration, wrapperDeclaration]);
137
+ },
119
138
 
120
- const newDeclarations = [];
121
- let hasTransformation = false;
139
+ VariableDeclaration(nodePath, state) {
140
+ const declarations = nodePath.node.declarations;
122
141
 
123
- for (const declarator of declarations) {
124
- const init = declarator.init;
142
+ if (!declarations || declarations.length === 0) {
143
+ return;
144
+ }
125
145
 
126
- if (!init ||
127
- !t.isCallExpression(init) ||
128
- !t.isIdentifier(init.callee, { name: 'require' }) ||
129
- !init.arguments[0] ||
130
- !t.isStringLiteral(init.arguments[0])) {
131
- newDeclarations.push(declarator);
132
- continue;
146
+ if (nodePath.node._transformed) {
147
+ return;
133
148
  }
134
149
 
135
- hasTransformation = true;
136
- const source = init.arguments[0].value;
137
- const currentFilePath = state.filename || process.cwd();
138
- const currentDir = path.dirname(currentFilePath);
150
+ const newDeclarations = [];
151
+ let hasTransformation = false;
152
+
153
+ for (const declarator of declarations) {
154
+ const init = declarator.init;
155
+
156
+ if (!init ||
157
+ !t.isCallExpression(init) ||
158
+ !t.isIdentifier(init.callee, {name: 'require'}) ||
159
+ !init.arguments[0] ||
160
+ !t.isStringLiteral(init.arguments[0])) {
161
+ newDeclarations.push(declarator);
162
+ continue;
163
+ }
164
+
165
+ const source = init.arguments[0].value;
166
+ const currentFilePath = state.filename || process.cwd();
167
+ const currentDir = path.dirname(currentFilePath);
168
+
169
+ let absolutePath;
170
+ if (source.startsWith(BABEL.PERIOD)) {
171
+ absolutePath = path.resolve(currentDir, source);
172
+ } else {
173
+ absolutePath = source;
174
+ }
175
+
176
+ if (mockedPaths && !mockedPaths.has(absolutePath)) {
177
+ newDeclarations.push(declarator);
178
+ continue;
179
+ }
180
+
181
+ hasTransformation = true;
182
+ const originalVar = nodePath.scope.generateUidIdentifier('original');
183
+
184
+ /*
185
+
186
+ const _original = require('./random');
187
+ const random = (...args) => {
188
+ const module = mockStore.has('/path/to/random.js')
189
+ ? { ..._original, ...mockStore.get('/path/to/random.js') }
190
+ : _original;
191
+ return module.random(...args);
192
+ };
193
+ */
139
194
 
140
- let absolutePath;
141
- if (source.startsWith(BABEL.PERIOD)) {
142
- absolutePath = path.resolve(currentDir, source);
143
- } else {
144
- absolutePath = source;
145
- }
146
-
147
- /*
148
- const _original = require('./random');
149
- const _module = mockStore.has('/path/to/random.js')
150
- ? { ..._original, ...mockStore.get('/path/to/random.js') }
151
- : _original;
152
- */
153
-
154
- const originalVar = nodePath.scope.generateUidIdentifier('original');
155
- const originalDeclarator = t.variableDeclarator(
156
- originalVar,
157
- t.callExpression(
158
- t.identifier('require'),
159
- [t.stringLiteral(source)]
160
- )
161
- );
162
-
163
- const transformedRequire = t.conditionalExpression(
164
- t.callExpression(
165
- t.memberExpression(
166
- t.identifier(MOCK.STORE_NAME),
167
- t.identifier(BABEL.HAS)
168
- ),
169
- [t.stringLiteral(absolutePath)]
170
- ),
171
- t.objectExpression([
172
- t.spreadElement(originalVar),
173
- t.spreadElement(
195
+ newDeclarations.push(
196
+ t.variableDeclarator(
197
+ originalVar,
174
198
  t.callExpression(
175
- t.memberExpression(
176
- t.identifier(MOCK.STORE_NAME),
177
- t.identifier(BABEL.GET)
178
- ),
179
- [t.stringLiteral(absolutePath)]
199
+ t.identifier('require'),
200
+ [t.stringLiteral(source)]
180
201
  )
181
202
  )
182
- ]),
183
- originalVar
184
- );
185
-
186
- if (t.isObjectPattern(declarator.id) || t.isArrayPattern(declarator.id)) {
187
- const tempVar = nodePath.scope.generateUidIdentifier('module');
188
-
189
- newDeclarations.push(originalDeclarator);
190
-
191
- newDeclarations.push(
192
- t.variableDeclarator(tempVar, transformedRequire)
193
203
  );
194
204
 
195
- newDeclarations.push(
196
- t.variableDeclarator(declarator.id, tempVar)
197
- );
198
- } else {
199
- newDeclarations.push(originalDeclarator);
200
-
201
- newDeclarations.push(
202
- t.variableDeclarator(declarator.id, transformedRequire)
203
- );
205
+ if (t.isObjectPattern(declarator.id)) {
206
+ const properties = declarator.id.properties;
207
+
208
+ properties.forEach(prop => {
209
+ const key = prop.key.name;
210
+ const localName = prop.value.name;
211
+
212
+ newDeclarations.push(
213
+ t.variableDeclarator(
214
+ t.identifier(localName),
215
+ t.arrowFunctionExpression(
216
+ [t.restElement(t.identifier('args'))],
217
+ t.blockStatement([
218
+ t.variableDeclaration(BABEL.CONST, [
219
+ t.variableDeclarator(
220
+ t.identifier(BABEL.MODULE),
221
+ t.conditionalExpression(
222
+ t.callExpression(
223
+ t.memberExpression(
224
+ t.identifier(MOCK.STORE_NAME),
225
+ t.identifier(BABEL.HAS)
226
+ ),
227
+ [t.stringLiteral(absolutePath)]
228
+ ),
229
+ t.objectExpression([
230
+ t.spreadElement(originalVar),
231
+ t.spreadElement(
232
+ t.callExpression(
233
+ t.memberExpression(
234
+ t.identifier(MOCK.STORE_NAME),
235
+ t.identifier(BABEL.GET)
236
+ ),
237
+ [t.stringLiteral(absolutePath)]
238
+ )
239
+ )
240
+ ]),
241
+ originalVar
242
+ )
243
+ )
244
+ ]),
245
+ t.returnStatement(
246
+ t.callExpression(
247
+ t.memberExpression(
248
+ t.identifier(BABEL.MODULE),
249
+ t.identifier(key)
250
+ ),
251
+ [t.spreadElement(t.identifier('args'))]
252
+ )
253
+ )
254
+ ])
255
+ )
256
+ )
257
+ );
258
+ });
259
+ } else {
260
+ newDeclarations.push(
261
+ t.variableDeclarator(
262
+ declarator.id,
263
+ t.arrowFunctionExpression(
264
+ [t.restElement(t.identifier('args'))],
265
+ t.blockStatement([
266
+ t.variableDeclaration(BABEL.CONST, [
267
+ t.variableDeclarator(
268
+ t.identifier(BABEL.MODULE),
269
+ t.conditionalExpression(
270
+ t.callExpression(
271
+ t.memberExpression(
272
+ t.identifier(MOCK.STORE_NAME),
273
+ t.identifier(BABEL.HAS)
274
+ ),
275
+ [t.stringLiteral(absolutePath)]
276
+ ),
277
+ t.objectExpression([
278
+ t.spreadElement(originalVar),
279
+ t.spreadElement(
280
+ t.callExpression(
281
+ t.memberExpression(
282
+ t.identifier(MOCK.STORE_NAME),
283
+ t.identifier(BABEL.GET)
284
+ ),
285
+ [t.stringLiteral(absolutePath)]
286
+ )
287
+ )
288
+ ]),
289
+ originalVar
290
+ )
291
+ )
292
+ ]),
293
+ t.returnStatement(
294
+ t.callExpression(
295
+ t.memberExpression(
296
+ t.identifier(BABEL.MODULE),
297
+ t.identifier('default')
298
+ ),
299
+ [t.spreadElement(t.identifier('args'))]
300
+ )
301
+ )
302
+ ])
303
+ )
304
+ )
305
+ );
306
+ }
204
307
  }
205
- }
206
308
 
207
- if (hasTransformation) {
208
- nodePath.node.declarations = newDeclarations;
209
- nodePath.node._transformed = true;
309
+ if (hasTransformation) {
310
+ nodePath.node.declarations = newDeclarations;
311
+ nodePath.node._transformed = true;
312
+ }
210
313
  }
211
314
  }
212
- }
213
- };
214
- };
315
+ };
316
+ }
317
+ };
package/bin/cli.js CHANGED
@@ -7,6 +7,7 @@ import {findAllSourceFiles, findTestFiles} from "./utils/findFiles.js";
7
7
  import {green, red, yellow} from "../utils/consoleColor.js";
8
8
  import {getTestResultMsg} from "../utils/makeMessage.js";
9
9
  import {RESULT_TITLE} from "../constants.js";
10
+ import {collectMockedPaths} from "../src/mock/collectMocks.js";
10
11
 
11
12
  const getUserModuleType = () => {
12
13
  try {
@@ -38,19 +39,20 @@ const main = async () => {
38
39
  global[key] = jsTe[key];
39
40
  });
40
41
 
42
+ const testFiles = findTestFiles(process.cwd());
43
+ console.log(`\nFound ${green(testFiles.length)} test file(s)`);
44
+
45
+ const mockedPaths = collectMockedPaths(testFiles);
46
+
41
47
  const sourceFiles = findAllSourceFiles(process.cwd());
42
48
  for (const file of sourceFiles) {
43
- transformFiles(file);
49
+ transformFiles(file, mockedPaths);
44
50
  }
45
51
 
46
- const testFiles = findTestFiles(process.cwd());
47
-
48
- console.log(`\nFound ${green(testFiles.length)} test file(s)`);
49
-
50
52
  for (const file of testFiles) {
51
53
  console.log(`\n${yellow(file)}\n`);
54
+ transformFiles(file, mockedPaths);
52
55
 
53
- transformFiles(file);
54
56
  await import(path.resolve(file));
55
57
 
56
58
  const {passed, failed} = await jsTe.run();
@@ -6,13 +6,13 @@ import {red} from "../../utils/consoleColor.js";
6
6
 
7
7
  const originalFiles = new Map();
8
8
 
9
- export const transformFiles = (filePath) => {
9
+ export const transformFiles = (filePath, mockPath) => {
10
10
  const originalCode = fs.readFileSync(filePath, 'utf-8');
11
11
  originalFiles.set(filePath, originalCode);
12
12
 
13
13
  const transformed = transformSync(originalCode, {
14
14
  filename: filePath,
15
- plugins: [babelTransformImport],
15
+ plugins: [babelTransformImport(mockPath)],
16
16
  parserOpts: {
17
17
  sourceType: BABEL.MODULE,
18
18
  plugins: ['dynamicImport']
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dannysir/js-te",
3
- "version": "0.2.3",
3
+ "version": "0.3.1",
4
4
  "description": "JavaScript test library",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -13,7 +13,7 @@
13
13
  "./src/mock/store.js": "./src/mock/store.js"
14
14
  },
15
15
  "bin": {
16
- "js-te": "./bin/cli.js"
16
+ "js-te": "bin/cli.js"
17
17
  },
18
18
  "scripts": {
19
19
  "clean": "rimraf dist",
@@ -27,6 +27,7 @@
27
27
  "utils/",
28
28
  "src/",
29
29
  "babelTransformImport.js",
30
+ "babelCollectMocks.js",
30
31
  "constants.js"
31
32
  ],
32
33
  "keywords": [
@@ -52,7 +53,7 @@
52
53
  },
53
54
  "repository": {
54
55
  "type": "git",
55
- "url": "git+https://github.com/dannysir/js-te-package"
56
+ "url": "git+https://github.com/dannysir/js-te-package.git"
56
57
  },
57
58
  "bugs": {
58
59
  "url": "https://github.com/dannysir/js-te-package/issues"
@@ -0,0 +1,28 @@
1
+ import fs from 'fs';
2
+ import {transformSync} from "@babel/core";
3
+ import {BABEL} from "../../constants.js";
4
+ import {createMockCollectorPlugin} from "../../babelCollectMocks.js";
5
+
6
+ export const collectMockedPaths = (testFiles) => {
7
+ const mockedPaths = new Set();
8
+
9
+ for (const testFile of testFiles) {
10
+ const code = fs.readFileSync(testFile, 'utf-8');
11
+
12
+ try {
13
+ transformSync(code, {
14
+ filename: testFile,
15
+ plugins: [createMockCollectorPlugin(mockedPaths)],
16
+ parserOpts: {
17
+ sourceType: BABEL.MODULE,
18
+ plugins: ['dynamicImport']
19
+ }
20
+ });
21
+ } catch (error) {
22
+ // 파싱 에러는 무시 (어차피 테스트 실행 시 에러 발생)
23
+ console.warn(`Warning: Failed to scan ${testFile} for mocks`);
24
+ }
25
+ }
26
+
27
+ return mockedPaths;
28
+ };