@dannysir/js-te 0.1.2 → 0.2.0

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