Skip to content

Commit b7f0a28

Browse files
authored
Merge pull request #52 from klmhyeonwoo/feature/50
feat: Added Base64 related utility functions
2 parents 908e6a9 + 0d51bb0 commit b7f0a28

7 files changed

Lines changed: 122 additions & 1 deletion

File tree

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ A comprehensive collection of TypeScript utility functions for modern web develo
44

55
## Features
66

7-
- 🛠️ **Comprehensive**: String, object, cookie, number, validation, format, search query, device, and common utilities
7+
- 🛠️ **Comprehensive**: String, object, cookie, number, validation, format, search query, device, type and common utilities
88
- 📦 **Tree-shakable**: Import only what you need
99
- 🔒 **Type-safe**: Full TypeScript support with type definitions
1010
-**Lightweight**: Minimal dependencies and optimized for performance
@@ -70,6 +70,8 @@ const nullCheck = commonUtil.isNull(null); // true
7070
const notNull = commonUtil.isNull("hello"); // false
7171
await commonUtil.sleep(1000); // Pauses execution for 1 second
7272
const copied = await commonUtil.copyToClipboard("Hello, World!"); // true if successful
73+
const encoded = commonUtil.encodeBase64("Hello 한글!"); // Base64 encoded string
74+
const decoded = commonUtil.decodeBase64(encoded); // "Hello 한글!"
7375

7476
// Search Query utilities
7577
const queryParams = searchQueryUtil.getAllQuery(); // { key: ["value1", "value2"], id: "123" }
@@ -125,6 +127,8 @@ const formattedPhone = formatUtil.formatPhoneNumber("01012345678"); // "010-1234
125127
- `isNull(value: unknown): value is null` - Type guard that checks if a value is null and narrows the type
126128
- `sleep(ms: number): Promise<void>` - Pauses execution for a specified number of milliseconds
127129
- `copyToClipboard(text: string): Promise<boolean>` - Copies text to the user's clipboard. Uses modern Clipboard API with fallback to legacy execCommand method. Returns true if successful, false if failed.
130+
- `encodeBase64(str: string, options?: { convertSpecialChars?: boolean }): string` - Encodes a string to Base64 format with optional special character handling
131+
- `decodeBase64(str: string, options?: { convertSpecialChars?: boolean }): string` - Decodes a Base64 string back to original text with optional special character handling
128132

129133
### SearchQueryUtil
130134

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { describe, expect, test } from "vitest";
2+
import decodeBase64 from ".";
3+
4+
describe("decodeBase64 유틸 함수 테스트", () => {
5+
test("정상적인 Base64 문자열을 디코딩해야 한다", () => {
6+
const result = decodeBase64("SGVsbG8sJTIwV29ybGQh");
7+
expect(result).toBe("Hello, World!");
8+
});
9+
test("빈 문자열을 디코딩하면 빈 문자열을 반환해야 한다", () => {
10+
const result = decodeBase64("");
11+
expect(result).toBe("");
12+
});
13+
test("특수 문자가 포함된 Base64 문자열을 올바르게 디코딩해야 한다", () => {
14+
const result = decodeBase64(
15+
"ISU0MCUyMyUyNCUyNSU1RSUyNiooKV8lMkIlMkYlM0IlMkMlM0YlM0ElNDAlMjY="
16+
);
17+
expect(result).toBe("!@#$%^&*()_+/;,?:@&");
18+
});
19+
test("유니코드 문자가 포함된 Base64 문자열을 올바르게 디코딩해야 한다", () => {
20+
const result = decodeBase64(
21+
"JUUzJTgxJTkzJUUzJTgyJTkzJUUzJTgxJUFCJUUzJTgxJUExJUUzJTgxJUFG"
22+
);
23+
expect(result).toBe("こんにちは");
24+
});
25+
test("null 또는 undefined를 디코딩하면 그대로 반환해야 한다", () => {
26+
// @ts-ignore
27+
expect(decodeBase64(null)).toBe(null);
28+
// @ts-ignore
29+
expect(decodeBase64(undefined)).toBe(undefined);
30+
});
31+
test("한글이 들어오면 올바르게 디코딩해야 한다", () => {
32+
const result = decodeBase64(
33+
"JUVDJTk1JTg4JUVCJTg1JTk1JUVEJTk1JTk4JUVDJTg0JUI4JUVDJTlBJTk0"
34+
);
35+
expect(result).toBe("안녕하세요");
36+
});
37+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
interface decodeBase64Options {
2+
convertSpecialChars?: boolean;
3+
}
4+
5+
export default function decodeBase64(
6+
str: string,
7+
{ convertSpecialChars = true }: decodeBase64Options = {}
8+
): string {
9+
if (str == null) return str;
10+
11+
if (convertSpecialChars) {
12+
return decodeURIComponent(atob(str));
13+
}
14+
return decodeURI(atob(str));
15+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { describe, expect, test } from "vitest";
2+
import encodeBase64 from ".";
3+
4+
describe("encodeBase64 유틸 함수 테스트", () => {
5+
test("정상적인 문자열을 Base64로 인코딩해야 한다", () => {
6+
const result = encodeBase64("Hello, World!");
7+
expect(result).toBe("SGVsbG8lMkMlMjBXb3JsZCE=");
8+
});
9+
test("빈 문자열을 인코딩하면 빈 문자열을 반환해야 한다", () => {
10+
const result = encodeBase64("");
11+
expect(result).toBe("");
12+
});
13+
test("특수 문자가 포함된 문자열을 올바르게 인코딩해야 한다", () => {
14+
const result = encodeBase64("!@#$%^&*()_+");
15+
expect(result).toBe("ISU0MCUyMyUyNCUyNSU1RSUyNiooKV8lMkI=");
16+
});
17+
test("유니코드 문자가 포함된 문자열을 올바르게 인코딩해야 한다", () => {
18+
const result = encodeBase64("こんにちは");
19+
expect(result).toBe(
20+
"JUUzJTgxJTkzJUUzJTgyJTkzJUUzJTgxJUFCJUUzJTgxJUExJUUzJTgxJUFG"
21+
);
22+
});
23+
test("null 또는 undefined를 인코딩하면 그대로 반환해야 한다", () => {
24+
// @ts-ignore
25+
expect(encodeBase64(null)).toBe(null);
26+
// @ts-ignore
27+
expect(encodeBase64(undefined)).toBe(undefined);
28+
});
29+
test("한글이 들어오면 올바르게 인코딩해야 한다", () => {
30+
const result = encodeBase64("안녕하세요");
31+
expect(result).toBe(
32+
"JUVDJTk1JTg4JUVCJTg1JTk1JUVEJTk1JTk4JUVDJTg0JUI4JUVDJTlBJTk0"
33+
);
34+
});
35+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
interface encodeBase64Options {
2+
convertSpecialChars?: boolean;
3+
}
4+
5+
export default function encodeBase64(
6+
str: string,
7+
{ convertSpecialChars = true }: encodeBase64Options = {}
8+
): string {
9+
if (str == null) return str;
10+
11+
if (convertSpecialChars) {
12+
return btoa(encodeURIComponent(str));
13+
}
14+
return btoa(encodeURI(str));
15+
}

package/commonUtil/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ export { default as isEmpty } from "./isEmpty";
22
export { default as isNull } from "./isNull";
33
export { default as sleep } from "./sleep";
44
export { default as copyToClipboard } from "./copyToClipboard";
5+
export { default as encodeBase64 } from "./encodeBase64";
6+
export { default as decodeBase64 } from "./decodeBase64";

vitest-report.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
11
<?xml version="1.0" encoding="UTF-8" ?>
2+
<testsuites name="vitest tests" tests="6" failures="0" errors="0" time="0.002156208">
3+
<testsuite name="package/commonUtil/encodeBase64/indes.test.ts" timestamp="2025-09-25T14:42:58.765Z" hostname="klmhyeonwooui-MacBookPro.local" tests="6" failures="0" errors="0" skipped="0" time="0.002156208">
4+
<testcase classname="package/commonUtil/encodeBase64/indes.test.ts" name="encodeBase64 유틸 함수 테스트 &gt; 정상적인 문자열을 Base64로 인코딩해야 한다" time="0.000807042">
5+
</testcase>
6+
<testcase classname="package/commonUtil/encodeBase64/indes.test.ts" name="encodeBase64 유틸 함수 테스트 &gt; 빈 문자열을 인코딩하면 빈 문자열을 반환해야 한다" time="0.000093625">
7+
</testcase>
8+
<testcase classname="package/commonUtil/encodeBase64/indes.test.ts" name="encodeBase64 유틸 함수 테스트 &gt; 특수 문자가 포함된 문자열을 올바르게 인코딩해야 한다" time="0.000173709">
9+
</testcase>
10+
<testcase classname="package/commonUtil/encodeBase64/indes.test.ts" name="encodeBase64 유틸 함수 테스트 &gt; 유니코드 문자가 포함된 문자열을 올바르게 인코딩해야 한다" time="0.000156042">
11+
</testcase>
12+
<testcase classname="package/commonUtil/encodeBase64/indes.test.ts" name="encodeBase64 유틸 함수 테스트 &gt; null 또는 undefined를 인코딩하면 그대로 반환해야 한다" time="0.000235916">
13+
</testcase>
14+
<testcase classname="package/commonUtil/encodeBase64/indes.test.ts" name="encodeBase64 유틸 함수 테스트 &gt; 한글이 들어오면 올바르게 인코딩해야 한다" time="0.000069459">
215
<testsuites name="vitest tests" tests="111" failures="0" errors="0" time="0.042221709">
316
<testsuite name="package/commonUtil/copyToClipboard/index.test.ts" timestamp="2025-09-21T02:31:20.170Z" hostname="192.168.nate.com" tests="5" failures="0" errors="0" skipped="0" time="0.003149416">
417
<testcase classname="package/commonUtil/copyToClipboard/index.test.ts" name="copyToClipboard &gt; 성공 케이스 &gt; 최신 Clipboard API를 사용하여 성공적으로 복사한다" time="0.001617584">

0 commit comments

Comments
 (0)