@dannysir/js-te 0.1.1 → 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,6 +362,28 @@ 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
389
  - [GitHub](https://github.com/dannysir/Js-Te)
@@ -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
  };
package/bin/cli.js CHANGED
@@ -1,108 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import fs from 'fs';
4
3
  import path from 'path';
5
- import {transformSync} from '@babel/core';
6
4
  import * as jsTe from '../index.js';
5
+ import {restoreFiles, transformFiles} from "./utils/transformFiles.js";
6
+ 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
- import {BABEL, PATH, RESULT_TITLE} from "../constants.js";
10
- import { babelTransformImport } from '../babelTransformImport.js';
11
-
12
- let totalPassed = 0;
13
- let totalFailed = 0;
14
- const originalFiles = new Map();
15
-
16
- Object.keys(jsTe).forEach(key => {
17
- global[key] = jsTe[key];
18
- });
19
-
20
- const transformFile = (filePath) => {
21
- const originalCode = fs.readFileSync(filePath, 'utf-8');
22
- originalFiles.set(filePath, originalCode);
23
-
24
- const transformed = transformSync(originalCode, {
25
- filename: filePath,
26
- plugins: [babelTransformImport],
27
- parserOpts: {
28
- sourceType: BABEL.MODULE,
29
- plugins: ['dynamicImport']
30
- }
31
- });
32
-
33
- fs.writeFileSync(filePath, transformed.code);
34
- };
35
-
36
- const restoreFiles = () => {
37
- for (const [filePath, originalCode] of originalFiles.entries()) {
38
- try {
39
- fs.writeFileSync(filePath, originalCode);
40
- } catch (error) {
41
- console.error(red(`Failed to restore ${filePath}: ${error.message}`));
42
- }
43
- }
44
- originalFiles.clear();
45
- };
46
-
47
- const findTestFiles = (dir) => {
48
- const files = [];
49
-
50
- const walk = (directory, inTestDir = false) => {
51
- const items = fs.readdirSync(directory);
52
- const dirName = path.basename(directory);
53
-
54
- const isTestDir = dirName === PATH.TEST_DIRECTORY || inTestDir;
55
-
56
- for (const item of items) {
57
- if (item === PATH.NODE_MODULES) continue;
58
-
59
- const fullPath = path.join(directory, item);
60
- const stat = fs.statSync(fullPath);
61
-
62
- if (stat.isDirectory()) {
63
- walk(fullPath, isTestDir);
64
- } else if (item.endsWith(PATH.TEST_FILE) || isTestDir) {
65
- if (item.endsWith(PATH.JAVASCRIPT_FILE)) {
66
- files.push(fullPath);
67
- }
68
- }
69
- }
70
- }
71
-
72
- walk(dir);
73
- return files;
74
- }
75
-
76
- const findAllSourceFiles = (dir) => {
77
- const files = [];
78
-
79
- const walk = (directory) => {
80
- const items = fs.readdirSync(directory);
81
-
82
- for (const item of items) {
83
- if (item === PATH.NODE_MODULES || item === PATH.BIN || item === PATH.TEST_DIRECTORY) continue;
84
-
85
- const fullPath = path.join(directory, item);
86
- const stat = fs.statSync(fullPath);
87
-
88
- if (stat.isDirectory()) {
89
- walk(fullPath);
90
- } else if (item.endsWith(PATH.JAVASCRIPT_FILE) && !item.endsWith(PATH.TEST_FILE)) {
91
- files.push(fullPath);
92
- }
93
- }
94
- }
95
-
96
- walk(dir);
97
- return files;
98
- }
99
-
9
+ import {RESULT_TITLE} from "../constants.js";
10
+ import {run} from "../src/testRunner.js";
100
11
 
101
12
  const main = async () => {
102
13
  try {
14
+ let totalPassed = 0;
15
+ let totalFailed = 0;
16
+
17
+ Object.keys(jsTe).forEach(key => {
18
+ global[key] = jsTe[key];
19
+ });
20
+
103
21
  const sourceFiles = findAllSourceFiles(process.cwd());
104
22
  for (const file of sourceFiles) {
105
- transformFile(file);
23
+ transformFiles(file);
106
24
  }
107
25
 
108
26
  const testFiles = findTestFiles(process.cwd());
@@ -112,10 +30,10 @@ const main = async () => {
112
30
  for (const file of testFiles) {
113
31
  console.log(`\n${yellow(file)}\n`);
114
32
 
115
- transformFile(file);
33
+ transformFiles(file);
116
34
  await import(path.resolve(file));
117
35
 
118
- const {passed, failed} = await jsTe.run();
36
+ const {passed, failed} = await run();
119
37
  totalPassed += passed;
120
38
  totalFailed += failed;
121
39
  }
@@ -0,0 +1,56 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import {PATH} from "../../constants.js";
4
+
5
+ export const findTestFiles = (dir) => {
6
+ const files = [];
7
+
8
+ const walk = (directory, inTestDir = false) => {
9
+ const items = fs.readdirSync(directory);
10
+ const dirName = path.basename(directory);
11
+
12
+ const isTestDir = dirName === PATH.TEST_DIRECTORY || inTestDir;
13
+
14
+ for (const item of items) {
15
+ if (item === PATH.NODE_MODULES) continue;
16
+
17
+ const fullPath = path.join(directory, item);
18
+ const stat = fs.statSync(fullPath);
19
+
20
+ if (stat.isDirectory()) {
21
+ walk(fullPath, isTestDir);
22
+ } else if (item.endsWith(PATH.TEST_FILE) || isTestDir) {
23
+ if (item.endsWith(PATH.JAVASCRIPT_FILE)) {
24
+ files.push(fullPath);
25
+ }
26
+ }
27
+ }
28
+ }
29
+
30
+ walk(dir);
31
+ return files;
32
+ }
33
+
34
+ export const findAllSourceFiles = (dir) => {
35
+ const files = [];
36
+
37
+ const walk = (directory) => {
38
+ const items = fs.readdirSync(directory);
39
+
40
+ for (const item of items) {
41
+ if (item === PATH.NODE_MODULES || item === PATH.BIN || item === PATH.TEST_DIRECTORY || item === PATH.DIST) continue;
42
+
43
+ const fullPath = path.join(directory, item);
44
+ const stat = fs.statSync(fullPath);
45
+
46
+ if (stat.isDirectory()) {
47
+ walk(fullPath);
48
+ } else if (item.endsWith(PATH.JAVASCRIPT_FILE) && !item.endsWith(PATH.TEST_FILE)) {
49
+ files.push(fullPath);
50
+ }
51
+ }
52
+ }
53
+
54
+ walk(dir);
55
+ return files;
56
+ }
@@ -0,0 +1,34 @@
1
+ import fs from 'fs';
2
+ import {transformSync} from "@babel/core";
3
+ import {babelTransformImport} from "../../babelTransformImport.js";
4
+ import {BABEL} from "../../constants.js";
5
+ import {red} from "../../utils/consoleColor.js";
6
+
7
+ const originalFiles = new Map();
8
+
9
+ export const transformFiles = (filePath) => {
10
+ const originalCode = fs.readFileSync(filePath, 'utf-8');
11
+ originalFiles.set(filePath, originalCode);
12
+
13
+ const transformed = transformSync(originalCode, {
14
+ filename: filePath,
15
+ plugins: [babelTransformImport],
16
+ parserOpts: {
17
+ sourceType: BABEL.MODULE,
18
+ plugins: ['dynamicImport']
19
+ }
20
+ });
21
+
22
+ fs.writeFileSync(filePath, transformed.code);
23
+ };
24
+
25
+ export const restoreFiles = () => {
26
+ for (const [filePath, originalCode] of originalFiles.entries()) {
27
+ try {
28
+ fs.writeFileSync(filePath, originalCode);
29
+ } catch (error) {
30
+ console.error(red(`Failed to restore ${filePath}: ${error.message}`));
31
+ }
32
+ }
33
+ originalFiles.clear();
34
+ };
package/constants.js CHANGED
@@ -1,7 +1,3 @@
1
- export const getErrorMsg = (expect, actual) => {
2
- return `Expected ${JSON.stringify(expect)} but got ${JSON.stringify(actual)}`;
3
- };
4
-
5
1
  export const RESULT_TITLE = {
6
2
  TESTS: 'Tests: ',
7
3
  TOTAL : 'Total Result: '
@@ -46,5 +42,5 @@ export const PATH = {
46
42
  TEST_FILE: '.test.js',
47
43
  JAVASCRIPT_FILE: '.js',
48
44
  BIN: 'bin',
49
-
50
- };
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.1",
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.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/tests.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/issues"
41
- },
42
- "homepage": "https://github.com/dannysir/Js-Te#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
+ }
@@ -1,6 +1,6 @@
1
1
  import {DIRECTORY_DELIMITER} from "../constants.js";
2
2
 
3
- export class Tests {
3
+ class TestManager {
4
4
  #tests = [];
5
5
  #testDepth = [];
6
6
  #beforeEachArr = [];
@@ -69,4 +69,6 @@ export class Tests {
69
69
  }
70
70
  });
71
71
  }
72
- }
72
+ }
73
+
74
+ export const testManager = new TestManager();
@@ -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
+ };
@@ -0,0 +1,21 @@
1
+ import {CHECK, CROSS, DIRECTORY_DELIMITER, EMPTY} from "../constants.js";
2
+ import {green, red} from "./consoleColor.js";
3
+
4
+ export const formatSuccessMessage = (test) => {
5
+ const pathString = test.path === '' ? EMPTY : test.path + DIRECTORY_DELIMITER;
6
+ return green(CHECK) + pathString + test.description;
7
+ };
8
+
9
+ export const formatFailureMessage = (test, error) => {
10
+ const messages = [];
11
+ messages.push(red(CROSS) + test.path + test.description);
12
+ messages.push(red(` Error Message : ${error.message}`));
13
+ return messages.join('\n');
14
+ };
15
+
16
+ export const getErrorMsg = (expect, actual) => {
17
+ return `Expected ${JSON.stringify(expect)} but got ${JSON.stringify(actual)}`;
18
+ };
19
+ export const getThrowErrorMsg = (expect) => {
20
+ return `Expected function to throw an error containing "${expect}", but it did not throw`;
21
+ };
package/index.js DELETED
@@ -1,94 +0,0 @@
1
- import {
2
- CHECK,
3
- CROSS, DEFAULT_COUNT, DIRECTORY_DELIMITER, EMPTY,
4
- getErrorMsg, RESULT_TITLE,
5
- } from "./constants.js";
6
- import {Tests} from "./src/tests.js";
7
- import {green, red} from "./utils/consoleColor.js";
8
- import {getTestResultMsg} from "./utils/makeMessage.js";
9
- import {clearAllMocks} from "./src/mock/store.js";
10
-
11
- const tests = new Tests();
12
-
13
- export { mock, clearAllMocks, unmock, isMocked } from './src/mock/store.js';
14
-
15
- export const test = (description, fn) => tests.test(description, fn);
16
- test.each = (cases) => tests.testEach(cases);
17
-
18
- export const describe = (suiteName, fn) => tests.describe(suiteName, fn);
19
-
20
- export const beforeEach = (fn) => tests.beforeEach(fn);
21
-
22
- export const expect = (actual) => {
23
- let value = actual;
24
-
25
- const runArgFnc = (actual) => {
26
- let value = actual;
27
- if (typeof actual === 'function') {
28
- value = actual();
29
- }
30
- return value;
31
- };
32
-
33
- return {
34
- toBe(expected) {
35
- value = runArgFnc(actual);
36
- if (value !== expected) {
37
- throw new Error(getErrorMsg(expected, value));
38
- }
39
- },
40
- toEqual(expected) {
41
- value = runArgFnc(actual);
42
- if (JSON.stringify(value) !== JSON.stringify(expected)) {
43
- throw new Error(getErrorMsg(expected, value));
44
- }
45
- },
46
- toThrow(expected) {
47
- try {
48
- value = runArgFnc(actual);
49
- } catch (e) {
50
- if (!e.message.includes(expected)) {
51
- throw new Error(getErrorMsg(expected, e.message));
52
- } else return;
53
- }
54
- },
55
- toBeTruthy() {
56
- value = runArgFnc(actual);
57
- if (!value) {
58
- throw new Error(getErrorMsg(true, value));
59
- }
60
- },
61
- toBeFalsy() {
62
- value = runArgFnc(actual);
63
- if (value) {
64
- throw new Error(getErrorMsg(true, value));
65
- }
66
- },
67
- };
68
- }
69
-
70
- export const run = async () => {
71
- let passed = DEFAULT_COUNT;
72
- let failed = DEFAULT_COUNT;
73
-
74
- for (const test of tests.getTests()) {
75
- try {
76
- await test.fn();
77
- const directoryString = green(CHECK) + (test.path === '' ? EMPTY : test.path + DIRECTORY_DELIMITER) + test.description
78
- console.log(directoryString);
79
- passed++;
80
- clearAllMocks();
81
- } catch (error) {
82
- const errorDirectory = red(CROSS) + test.path + test.description
83
- console.log(errorDirectory);
84
- console.log(red(` Error Message : ${error.message}`));
85
- failed++;
86
- }
87
- }
88
-
89
- console.log(getTestResultMsg(RESULT_TITLE.TESTS, passed, failed));
90
-
91
- tests.clearTests();
92
-
93
- return {passed, failed};
94
- }