@dannysir/js-te 0.1.0 → 0.1.2
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 +7 -2
- package/bin/cli.js +35 -103
- package/bin/utils/findFiles.js +56 -0
- package/bin/utils/transformFiles.js +34 -0
- package/constants.js +0 -5
- package/index.js +9 -89
- package/package.json +5 -5
- package/src/{tests.js → testManager.js} +4 -2
- package/utils/formatString.js +21 -0
package/README.md
CHANGED
|
@@ -44,6 +44,11 @@ package.json에 추가.
|
|
|
44
44
|
npm test
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
+
### 예시 출력 화면
|
|
48
|
+
|
|
49
|
+
<p align='center'>
|
|
50
|
+
<img width="585" height="902" alt="스크린샷 2025-11-20 오후 12 22 27" src="https://github.com/user-attachments/assets/3d087a61-cc44-4f5b-8a2f-efd5f15c12b7" />
|
|
51
|
+
</p>
|
|
47
52
|
|
|
48
53
|
# API
|
|
49
54
|
|
|
@@ -319,7 +324,7 @@ export const random = () => Math.random();
|
|
|
319
324
|
|
|
320
325
|
## 링크
|
|
321
326
|
|
|
322
|
-
- [GitHub](https://github.com/dannysir/
|
|
327
|
+
- [GitHub](https://github.com/dannysir/js-te-package)
|
|
323
328
|
|
|
324
329
|
## 만든 이유
|
|
325
330
|
|
|
@@ -327,4 +332,4 @@ export const random = () => Math.random();
|
|
|
327
332
|
|
|
328
333
|
## 라이선스
|
|
329
334
|
|
|
330
|
-
ISC
|
|
335
|
+
ISC
|
package/bin/cli.js
CHANGED
|
@@ -1,123 +1,55 @@
|
|
|
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';
|
|
7
|
-
import {
|
|
5
|
+
import {restoreFiles, transformFiles} from "./utils/transformFiles.js";
|
|
6
|
+
import {findAllSourceFiles, findTestFiles} from "./utils/findFiles.js";
|
|
7
|
+
import {green, red, yellow} from "../utils/consoleColor.js";
|
|
8
8
|
import {getTestResultMsg} from "../utils/makeMessage.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import {RESULT_TITLE} from "../constants.js";
|
|
10
|
+
import {run} from "../src/testRunner.js";
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
const main = async () => {
|
|
13
|
+
try {
|
|
14
|
+
let totalPassed = 0;
|
|
15
|
+
let totalFailed = 0;
|
|
15
16
|
|
|
16
|
-
Object.keys(jsTe).forEach(key => {
|
|
17
|
-
|
|
18
|
-
});
|
|
17
|
+
Object.keys(jsTe).forEach(key => {
|
|
18
|
+
global[key] = jsTe[key];
|
|
19
|
+
});
|
|
19
20
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const transformed = transformSync(originalCode, {
|
|
25
|
-
filename: filePath,
|
|
26
|
-
plugins: [babelTransformImport],
|
|
27
|
-
parserOpts: {
|
|
28
|
-
sourceType: BABEL.MODULE,
|
|
29
|
-
plugins: ['dynamicImport']
|
|
21
|
+
const sourceFiles = findAllSourceFiles(process.cwd());
|
|
22
|
+
for (const file of sourceFiles) {
|
|
23
|
+
transformFiles(file);
|
|
30
24
|
}
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
fs.writeFileSync(filePath, transformed.code);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const restoreFiles = () => {
|
|
37
|
-
for (const [filePath, originalCode] of originalFiles.entries()) {
|
|
38
|
-
fs.writeFileSync(filePath, originalCode);
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
25
|
|
|
42
|
-
const
|
|
43
|
-
const files = [];
|
|
26
|
+
const testFiles = findTestFiles(process.cwd());
|
|
44
27
|
|
|
45
|
-
|
|
46
|
-
const items = fs.readdirSync(directory);
|
|
47
|
-
const dirName = path.basename(directory);
|
|
28
|
+
console.log(`\nFound ${green(testFiles.length)} test file(s)`);
|
|
48
29
|
|
|
49
|
-
const
|
|
30
|
+
for (const file of testFiles) {
|
|
31
|
+
console.log(`\n${yellow(file)}\n`);
|
|
50
32
|
|
|
51
|
-
|
|
52
|
-
|
|
33
|
+
transformFiles(file);
|
|
34
|
+
await import(path.resolve(file));
|
|
53
35
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (stat.isDirectory()) {
|
|
58
|
-
walk(fullPath, isTestDir);
|
|
59
|
-
} else if (item.endsWith(PATH.TEST_FILE) || isTestDir) {
|
|
60
|
-
if (item.endsWith(PATH.JAVASCRIPT_FILE)) {
|
|
61
|
-
files.push(fullPath);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
36
|
+
const {passed, failed} = await run();
|
|
37
|
+
totalPassed += passed;
|
|
38
|
+
totalFailed += failed;
|
|
64
39
|
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
walk(dir);
|
|
68
|
-
return files;
|
|
69
|
-
}
|
|
70
40
|
|
|
71
|
-
|
|
72
|
-
const files = [];
|
|
41
|
+
console.log(getTestResultMsg(RESULT_TITLE.TOTAL, totalPassed, totalFailed));
|
|
73
42
|
|
|
74
|
-
|
|
75
|
-
const items = fs.readdirSync(directory);
|
|
43
|
+
return totalFailed > 0 ? 1 : 0;
|
|
76
44
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (stat.isDirectory()) {
|
|
84
|
-
walk(fullPath);
|
|
85
|
-
} else if (item.endsWith(PATH.JAVASCRIPT_FILE) && !item.endsWith(PATH.TEST_FILE)) {
|
|
86
|
-
files.push(fullPath);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.log(red('\n✗ Test execution failed'));
|
|
47
|
+
console.log(red(` Error: ${error.message}\n`));
|
|
48
|
+
return 1;
|
|
49
|
+
} finally {
|
|
50
|
+
restoreFiles();
|
|
89
51
|
}
|
|
52
|
+
};
|
|
90
53
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const sourceFiles = findAllSourceFiles(process.cwd());
|
|
96
|
-
|
|
97
|
-
for (const file of sourceFiles) {
|
|
98
|
-
transformFile(file);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const testFiles = findTestFiles(process.cwd());
|
|
102
|
-
|
|
103
|
-
console.log(`\nFound ${green(testFiles.length)} test file(s)`);
|
|
104
|
-
|
|
105
|
-
for (const file of testFiles) {
|
|
106
|
-
console.log(`\n${yellow(file)}\n`);
|
|
107
|
-
|
|
108
|
-
transformFile(file);
|
|
109
|
-
|
|
110
|
-
await import(path.resolve(file));
|
|
111
|
-
|
|
112
|
-
const {passed, failed} = await jsTe.run();
|
|
113
|
-
totalPassed += passed;
|
|
114
|
-
totalFailed += failed;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
restoreFiles();
|
|
118
|
-
|
|
119
|
-
console.log(getTestResultMsg(RESULT_TITLE.TOTAL, totalPassed, totalFailed));
|
|
120
|
-
|
|
121
|
-
if (totalFailed > 0) {
|
|
122
|
-
process.exit(1);
|
|
123
|
-
}
|
|
54
|
+
const exitCode = await main();
|
|
55
|
+
process.exit(exitCode);
|
|
@@ -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) 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,4 @@ export const PATH = {
|
|
|
46
42
|
TEST_FILE: '.test.js',
|
|
47
43
|
JAVASCRIPT_FILE: '.js',
|
|
48
44
|
BIN: 'bin',
|
|
49
|
-
|
|
50
45
|
};
|
package/index.js
CHANGED
|
@@ -1,94 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
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";
|
|
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";
|
|
10
4
|
|
|
11
|
-
const
|
|
5
|
+
export const test = (description, fn) => testManager.test(description, fn);
|
|
6
|
+
test.each = (cases) => testManager.testEach(cases);
|
|
12
7
|
|
|
13
|
-
export
|
|
8
|
+
export const describe = (suiteName, fn) => testManager.describe(suiteName, fn);
|
|
14
9
|
|
|
15
|
-
export const
|
|
16
|
-
test.each = (cases) => tests.testEach(cases);
|
|
10
|
+
export const beforeEach = (fn) => testManager.beforeEach(fn);
|
|
17
11
|
|
|
18
|
-
export
|
|
12
|
+
export {expect};
|
|
19
13
|
|
|
20
|
-
export
|
|
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}`));
|
|
85
|
-
failed++;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
console.log(getTestResultMsg(RESULT_TITLE.TESTS, passed, failed));
|
|
90
|
-
|
|
91
|
-
tests.clearTests();
|
|
92
|
-
|
|
93
|
-
return {passed, failed};
|
|
94
|
-
}
|
|
14
|
+
export {mock, clearAllMocks, unmock, isMocked};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dannysir/js-te",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "JavaScript test library",
|
|
6
6
|
"main": "index.js",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/dannysir/
|
|
19
|
+
"url": "git+https://github.com/dannysir/js-te-package.git"
|
|
20
20
|
},
|
|
21
21
|
"keywords": [
|
|
22
22
|
"javascript",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
],
|
|
28
28
|
"files": [
|
|
29
29
|
"bin/",
|
|
30
|
-
"src/
|
|
30
|
+
"src/testManager.js",
|
|
31
31
|
"src/mock",
|
|
32
32
|
"utils/",
|
|
33
33
|
"index.js",
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
"author": "dannysir",
|
|
38
38
|
"license": "ISC",
|
|
39
39
|
"bugs": {
|
|
40
|
-
"url": "https://github.com/dannysir/
|
|
40
|
+
"url": "https://github.com/dannysir/js-te-package/issues"
|
|
41
41
|
},
|
|
42
|
-
"homepage": "https://github.com/dannysir/
|
|
42
|
+
"homepage": "https://github.com/dannysir/js-te-package#readme",
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@babel/core": "^7.28.5",
|
|
45
45
|
"@babel/generator": "^7.28.5",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {DIRECTORY_DELIMITER} from "../constants.js";
|
|
2
2
|
|
|
3
|
-
|
|
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,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
|
+
};
|