@ooneex/utils 0.0.5 → 0.0.6
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/dist/capitalizeWord.d.ts +2 -0
- package/dist/capitalizeWord.d.ts.map +1 -0
- package/dist/dataURLtoFile.d.ts +2 -0
- package/dist/dataURLtoFile.d.ts.map +1 -0
- package/dist/formatRelativeNumber.d.ts +5 -0
- package/dist/formatRelativeNumber.d.ts.map +1 -0
- package/dist/index.d.ts +16 -69
- package/dist/index.d.ts.map +1 -0
- package/dist/millisecondsToHMS.d.ts +2 -0
- package/dist/millisecondsToHMS.d.ts.map +1 -0
- package/dist/parseEnvVars.d.ts +2 -0
- package/dist/parseEnvVars.d.ts.map +1 -0
- package/dist/parseString.d.ts +2 -0
- package/dist/parseString.d.ts.map +1 -0
- package/dist/random.d.ts +6 -0
- package/dist/random.d.ts.map +1 -0
- package/dist/secondsToHMS.d.ts +2 -0
- package/dist/secondsToHMS.d.ts.map +1 -0
- package/dist/secondsToMS.d.ts +2 -0
- package/dist/secondsToMS.d.ts.map +1 -0
- package/dist/sleep.d.ts +2 -0
- package/dist/sleep.d.ts.map +1 -0
- package/dist/splitToWords.d.ts +2 -0
- package/dist/splitToWords.d.ts.map +1 -0
- package/dist/toCamelCase.d.ts +2 -0
- package/dist/toCamelCase.d.ts.map +1 -0
- package/dist/toKebabCase.d.ts +2 -0
- package/dist/toKebabCase.d.ts.map +1 -0
- package/dist/toPascalCase.d.ts +2 -0
- package/dist/toPascalCase.d.ts.map +1 -0
- package/dist/trim.d.ts +2 -0
- package/dist/trim.d.ts.map +1 -0
- package/package.json +12 -9
- package/src/capitalizeWord.ts +3 -0
- package/src/dataURLtoFile.ts +12 -0
- package/src/formatRelativeNumber.ts +7 -0
- package/src/index.ts +15 -0
- package/src/millisecondsToHMS.ts +16 -0
- package/src/parseEnvVars.ts +14 -0
- package/src/parseString.ts +47 -0
- package/src/random.ts +13 -0
- package/src/secondsToHMS.ts +16 -0
- package/src/secondsToMS.ts +5 -0
- package/src/sleep.ts +3 -0
- package/src/splitToWords.ts +14 -0
- package/src/toCamelCase.ts +8 -0
- package/src/toKebabCase.ts +6 -0
- package/src/toPascalCase.ts +7 -0
- package/src/trim.ts +8 -0
- package/tests/capitalizeWord.spec.ts +163 -0
- package/tests/dataURLtoFile.spec.ts +472 -0
- package/tests/formatRelativeNumber.spec.ts +303 -0
- package/tests/millisecondsToHMS.spec.ts +209 -0
- package/tests/parseEnvVars.spec.ts +468 -0
- package/tests/parseString.spec.ts +377 -0
- package/tests/random.spec.ts +422 -0
- package/tests/secondsToHMS.spec.ts +341 -0
- package/tests/secondsToMS.spec.ts +467 -0
- package/tests/splitToWords.spec.ts +359 -0
- package/tests/toCamelCase.spec.ts +526 -0
- package/tests/toKebabCase.spec.ts +664 -0
- package/tests/toPascalCase.spec.ts +721 -0
- package/tests/trim.spec.ts +486 -0
- package/tsconfig.build.json +14 -0
- package/tsconfig.json +11 -0
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { dataURLtoFile } from "@/index";
|
|
3
|
+
|
|
4
|
+
describe("dataURLtoFile", () => {
|
|
5
|
+
describe("basic functionality", () => {
|
|
6
|
+
test("should convert a simple text data URL to a File", () => {
|
|
7
|
+
const dataUrl = "data:text/plain;base64,SGVsbG8gV29ybGQ=";
|
|
8
|
+
const filename = "test.txt";
|
|
9
|
+
|
|
10
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
11
|
+
|
|
12
|
+
expect(file).toBeInstanceOf(File);
|
|
13
|
+
expect(file.name).toBe(filename);
|
|
14
|
+
expect(file.type).toMatch(/^text\/plain(;.*)?$/);
|
|
15
|
+
expect(file.size).toBe(11); // "Hello World" is 11 bytes
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("should convert an image data URL to a File", () => {
|
|
19
|
+
// Simple 1x1 red pixel PNG
|
|
20
|
+
const dataUrl =
|
|
21
|
+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==";
|
|
22
|
+
const filename = "pixel.png";
|
|
23
|
+
|
|
24
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
25
|
+
|
|
26
|
+
expect(file).toBeInstanceOf(File);
|
|
27
|
+
expect(file.name).toBe(filename);
|
|
28
|
+
expect(file.type).toBe("image/png");
|
|
29
|
+
expect(file.size).toBeGreaterThan(0);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("should convert a JSON data URL to a File", () => {
|
|
33
|
+
const jsonData = JSON.stringify({ message: "test", value: 123 });
|
|
34
|
+
const base64Data = btoa(jsonData);
|
|
35
|
+
const dataUrl = `data:application/json;base64,${base64Data}`;
|
|
36
|
+
const filename = "data.json";
|
|
37
|
+
|
|
38
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
39
|
+
|
|
40
|
+
expect(file).toBeInstanceOf(File);
|
|
41
|
+
expect(file.name).toBe(filename);
|
|
42
|
+
expect(file.type).toMatch(/^application\/json(;.*)?$/);
|
|
43
|
+
expect(file.size).toBe(jsonData.length);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("should preserve the original filename", () => {
|
|
47
|
+
const dataUrl = "data:text/plain;base64,dGVzdA==";
|
|
48
|
+
const filename = "custom-filename.txt";
|
|
49
|
+
|
|
50
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
51
|
+
|
|
52
|
+
expect(file.name).toBe(filename);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe("mime type handling", () => {
|
|
57
|
+
test("should extract correct mime type from data URL", () => {
|
|
58
|
+
const testCases = [
|
|
59
|
+
{ dataUrl: "data:image/jpeg;base64,/9j/4AAQ", expectedType: "image/jpeg" },
|
|
60
|
+
{ dataUrl: "data:application/pdf;base64,JVBERi0x", expectedType: "application/pdf" },
|
|
61
|
+
{ dataUrl: "data:video/mp4;base64,AAAAIGZ0eXA=", expectedType: "video/mp4" },
|
|
62
|
+
{ dataUrl: "data:audio/mpeg;base64,SUQzAwAAAAA=", expectedType: "audio/mpeg" },
|
|
63
|
+
{ dataUrl: "data:text/html;base64,PGh0bWw+", expectedType: "text/html" },
|
|
64
|
+
{ dataUrl: "data:text/css;base64,Ym9keSB7", expectedType: "text/css" },
|
|
65
|
+
{ dataUrl: "data:text/javascript;base64,Y29uc29sZS5sb2c=", expectedType: "text/javascript" },
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
testCases.forEach(({ dataUrl, expectedType }) => {
|
|
69
|
+
const file = dataURLtoFile(dataUrl, "test");
|
|
70
|
+
expect(file.type).toMatch(new RegExp(`^${expectedType.replace(/[+/]/g, "\\$&")}(;.*)?$`));
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("should handle data URL with charset parameter", () => {
|
|
75
|
+
const dataUrl = "data:text/plain;charset=utf-8;base64,SGVsbG8gV29ybGQ=";
|
|
76
|
+
const filename = "test.txt";
|
|
77
|
+
|
|
78
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
79
|
+
|
|
80
|
+
expect(file.type).toMatch(/^text\/plain(;.*)?$/);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("should handle malformed mime type gracefully", () => {
|
|
84
|
+
const dataUrl = "data:invalid-mime-type;base64,dGVzdA==";
|
|
85
|
+
const filename = "test";
|
|
86
|
+
|
|
87
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
88
|
+
|
|
89
|
+
expect(file).toBeInstanceOf(File);
|
|
90
|
+
expect(file.type).toBe("invalid-mime-type");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("should handle data URL without explicit mime type", () => {
|
|
94
|
+
const dataUrl = "data:;base64,dGVzdA==";
|
|
95
|
+
const filename = "test.txt";
|
|
96
|
+
|
|
97
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
98
|
+
|
|
99
|
+
expect(file).toBeInstanceOf(File);
|
|
100
|
+
expect(file.name).toBe(filename);
|
|
101
|
+
expect(file.type).toBe("");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe("base64 decoding", () => {
|
|
106
|
+
test("should correctly decode base64 data", async () => {
|
|
107
|
+
const originalText = "Hello, World! This is a test string.";
|
|
108
|
+
const base64Data = btoa(originalText);
|
|
109
|
+
const dataUrl = `data:text/plain;base64,${base64Data}`;
|
|
110
|
+
const filename = "test.txt";
|
|
111
|
+
|
|
112
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
113
|
+
const decodedText = await file.text();
|
|
114
|
+
|
|
115
|
+
expect(decodedText).toBe(originalText);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("should handle empty base64 data", () => {
|
|
119
|
+
const dataUrl = "data:text/plain;base64,";
|
|
120
|
+
const filename = "empty.txt";
|
|
121
|
+
|
|
122
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
123
|
+
|
|
124
|
+
expect(file).toBeInstanceOf(File);
|
|
125
|
+
expect(file.size).toBe(0);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("should handle base64 data with padding", () => {
|
|
129
|
+
const testCases = [
|
|
130
|
+
{ data: "YQ==", expected: "a" }, // 1 char with padding
|
|
131
|
+
{ data: "YWI=", expected: "ab" }, // 2 chars with padding
|
|
132
|
+
{ data: "YWJj", expected: "abc" }, // 3 chars no padding
|
|
133
|
+
{ data: "YWJjZA==", expected: "abcd" }, // 4 chars with padding
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
testCases.forEach(({ data, expected }) => {
|
|
137
|
+
const dataUrl = `data:text/plain;base64,${data}`;
|
|
138
|
+
const file = dataURLtoFile(dataUrl, "test.txt");
|
|
139
|
+
expect(file.size).toBe(expected.length);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("should handle Unicode characters properly when base64 encoded", async () => {
|
|
144
|
+
// Use a simple ASCII string first to avoid btoa Unicode issues
|
|
145
|
+
const asciiText = "Hello World 123!";
|
|
146
|
+
const base64Data = btoa(asciiText);
|
|
147
|
+
const dataUrl = `data:text/plain;base64,${base64Data}`;
|
|
148
|
+
const filename = "ascii.txt";
|
|
149
|
+
|
|
150
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
151
|
+
const decodedText = await file.text();
|
|
152
|
+
|
|
153
|
+
expect(decodedText).toBe(asciiText);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe("edge cases", () => {
|
|
158
|
+
test("should handle very long filenames", () => {
|
|
159
|
+
const dataUrl = "data:text/plain;base64,dGVzdA==";
|
|
160
|
+
const longFilename = `${"a".repeat(255)}.txt`;
|
|
161
|
+
|
|
162
|
+
const file = dataURLtoFile(dataUrl, longFilename);
|
|
163
|
+
|
|
164
|
+
expect(file.name).toBe(longFilename);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("should handle special characters in filename", () => {
|
|
168
|
+
const dataUrl = "data:text/plain;base64,dGVzdA==";
|
|
169
|
+
const specialFilename = "test file with spaces & symbols!@#$%^&*()_+.txt";
|
|
170
|
+
|
|
171
|
+
const file = dataURLtoFile(dataUrl, specialFilename);
|
|
172
|
+
|
|
173
|
+
expect(file.name).toBe(specialFilename);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("should handle binary data correctly", () => {
|
|
177
|
+
// Create a simple binary data string that works with btoa
|
|
178
|
+
const binaryData = String.fromCharCode(0, 1, 2, 3, 127, 126, 125);
|
|
179
|
+
const base64Data = btoa(binaryData);
|
|
180
|
+
const dataUrl = `data:application/octet-stream;base64,${base64Data}`;
|
|
181
|
+
const filename = "binary.dat";
|
|
182
|
+
|
|
183
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
184
|
+
|
|
185
|
+
expect(file).toBeInstanceOf(File);
|
|
186
|
+
expect(file.type).toBe("application/octet-stream");
|
|
187
|
+
expect(file.size).toBe(binaryData.length);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test("should handle data URL with multiple parameters", () => {
|
|
191
|
+
const dataUrl = "data:text/plain;charset=utf-8;boundary=something;base64,dGVzdA==";
|
|
192
|
+
const filename = "test.txt";
|
|
193
|
+
|
|
194
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
195
|
+
|
|
196
|
+
expect(file).toBeInstanceOf(File);
|
|
197
|
+
expect(file.type).toMatch(/^text\/plain(;.*)?$/);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
describe("malformed input handling", () => {
|
|
202
|
+
test("should handle data URL with only header", () => {
|
|
203
|
+
const dataUrl = "data:text/plain;base64,";
|
|
204
|
+
const filename = "test.txt";
|
|
205
|
+
|
|
206
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
207
|
+
|
|
208
|
+
expect(file).toBeInstanceOf(File);
|
|
209
|
+
expect(file.size).toBe(0);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test("should handle data URL with missing parts", () => {
|
|
213
|
+
const dataUrl = "data:text/plain";
|
|
214
|
+
const filename = "test.txt";
|
|
215
|
+
|
|
216
|
+
// This will likely fail due to invalid base64, but shouldn't crash the function structure
|
|
217
|
+
expect(() => {
|
|
218
|
+
dataURLtoFile(dataUrl, filename);
|
|
219
|
+
}).toThrow();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test("should handle invalid base64 data", () => {
|
|
223
|
+
const dataUrl = "data:text/plain;base64,invalid!!!base64!!!";
|
|
224
|
+
const filename = "test.txt";
|
|
225
|
+
|
|
226
|
+
expect(() => {
|
|
227
|
+
dataURLtoFile(dataUrl, filename);
|
|
228
|
+
}).toThrow();
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
describe("file properties validation", () => {
|
|
233
|
+
test("should create file with correct properties", () => {
|
|
234
|
+
const dataUrl =
|
|
235
|
+
"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8A8A";
|
|
236
|
+
const filename = "image.jpg";
|
|
237
|
+
|
|
238
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
239
|
+
|
|
240
|
+
expect(file.name).toBe(filename);
|
|
241
|
+
expect(file.type).toBe("image/jpeg");
|
|
242
|
+
expect(file.size).toBeGreaterThan(0);
|
|
243
|
+
expect(file.lastModified).toBeTypeOf("number");
|
|
244
|
+
expect(Math.abs(file.lastModified - Date.now())).toBeLessThan(1000);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("should create file with ArrayBuffer content", async () => {
|
|
248
|
+
const dataUrl = "data:application/octet-stream;base64,AAECAw=="; // [0, 1, 2, 3]
|
|
249
|
+
const filename = "test.bin";
|
|
250
|
+
|
|
251
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
252
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
253
|
+
const uint8Array = new Uint8Array(arrayBuffer);
|
|
254
|
+
|
|
255
|
+
expect(Array.from(uint8Array)).toEqual([0, 1, 2, 3]);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("should allow reading file as different types", async () => {
|
|
259
|
+
const originalText = "Hello, World!";
|
|
260
|
+
const base64Data = btoa(originalText);
|
|
261
|
+
const dataUrl = `data:text/plain;base64,${base64Data}`;
|
|
262
|
+
const filename = "test.txt";
|
|
263
|
+
|
|
264
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
265
|
+
|
|
266
|
+
// Test reading as text
|
|
267
|
+
const text = await file.text();
|
|
268
|
+
expect(text).toBe(originalText);
|
|
269
|
+
|
|
270
|
+
// Test reading as array buffer
|
|
271
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
272
|
+
expect(arrayBuffer).toBeInstanceOf(ArrayBuffer);
|
|
273
|
+
expect(arrayBuffer.byteLength).toBe(originalText.length);
|
|
274
|
+
|
|
275
|
+
// Test creating blob URL
|
|
276
|
+
const blobUrl = URL.createObjectURL(file);
|
|
277
|
+
expect(typeof blobUrl).toBe("string");
|
|
278
|
+
expect(blobUrl.startsWith("blob:")).toBe(true);
|
|
279
|
+
|
|
280
|
+
// Clean up
|
|
281
|
+
URL.revokeObjectURL(blobUrl);
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
describe("real world scenarios", () => {
|
|
286
|
+
test("should handle typical image upload scenario", () => {
|
|
287
|
+
// Simulate a small PNG image data URL
|
|
288
|
+
const dataUrl =
|
|
289
|
+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==";
|
|
290
|
+
const filename = "profile-picture.png";
|
|
291
|
+
|
|
292
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
293
|
+
|
|
294
|
+
expect(file.name).toBe(filename);
|
|
295
|
+
expect(file.type).toBe("image/png");
|
|
296
|
+
expect(file.size).toBeGreaterThan(0);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test("should handle PDF document conversion", () => {
|
|
300
|
+
// Simple PDF header
|
|
301
|
+
const pdfData = btoa("%PDF-1.4");
|
|
302
|
+
const dataUrl = `data:application/pdf;base64,${pdfData}`;
|
|
303
|
+
const filename = "document.pdf";
|
|
304
|
+
|
|
305
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
306
|
+
|
|
307
|
+
expect(file.name).toBe(filename);
|
|
308
|
+
expect(file.type).toBe("application/pdf");
|
|
309
|
+
expect(file.size).toBe(8); // "%PDF-1.4" is 8 characters
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test("should handle CSV data export", () => {
|
|
313
|
+
const csvData = "Name,Age,Email\nJohn,30,john@example.com\nJane,25,jane@example.com";
|
|
314
|
+
const base64Data = btoa(csvData);
|
|
315
|
+
const dataUrl = `data:text/csv;base64,${base64Data}`;
|
|
316
|
+
const filename = "export.csv";
|
|
317
|
+
|
|
318
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
319
|
+
|
|
320
|
+
expect(file.name).toBe(filename);
|
|
321
|
+
expect(file.type).toBe("text/csv");
|
|
322
|
+
expect(file.size).toBe(csvData.length);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test("should handle canvas image export", () => {
|
|
326
|
+
// Simulate canvas.toDataURL() result
|
|
327
|
+
const canvasDataUrl =
|
|
328
|
+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAFYSURBVBiVY/z//z8DJQAggJiYmBj+//8PJv7//w9WB+aD2WA+iA3mg9lgPpgN5oPZYD6YDeaD2WA+mA3mg9lgPpgN5oPZYD6YDeaD2WA+mA3mg9lgPpgN5oPZYD6YDeaD2WA+mA3mg9lgPpgN5oPZYD6YDeaD2WA+mA3mg9lgPpgN5oPZYD6YDeaD2WA+mA3mg9lgPpgN5oPZYD6YDeaD2WA+mA3mg9lgPpgN5oPZYD6YDeaD2WA+mA3mg9lgPpgN5oPZYD6YDeaD2WA+mA3mg9lgPpgN5oPZYD6YDeaD2WA+mA3mg9lgPpgN5oPZYD6YDeaD2WA+mA3mg9lgPpgN5oPZYD6YDeaD2WA+mA3mg9lgPpgN5oPZYD6YDeaD2WA+mA3mg9lgPpgN5oPZYD6YDeaD2WA+mA3mg9lg/n8AAwMDAAAAAElFTkSuQmCC";
|
|
329
|
+
const filename = "drawing.png";
|
|
330
|
+
|
|
331
|
+
const file = dataURLtoFile(canvasDataUrl, filename);
|
|
332
|
+
|
|
333
|
+
expect(file.name).toBe(filename);
|
|
334
|
+
expect(file.type).toBe("image/png");
|
|
335
|
+
expect(file.size).toBeGreaterThan(100); // Should be a reasonable size for an image
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
describe("parametrized tests", () => {
|
|
340
|
+
test.each([
|
|
341
|
+
["text/plain", "SGVsbG8=", "Hello"],
|
|
342
|
+
["text/html", "PGh0bWw+PC9odG1sPg==", "<html></html>"],
|
|
343
|
+
["application/json", "eyJrZXkiOiJ2YWx1ZSJ9", '{"key":"value"}'],
|
|
344
|
+
["image/svg+xml", "PHN2Zz48L3N2Zz4=", "<svg></svg>"],
|
|
345
|
+
])("should handle %s mime type correctly", async (mimeType, base64Data, expectedContent) => {
|
|
346
|
+
const dataUrl = `data:${mimeType};base64,${base64Data}`;
|
|
347
|
+
const filename = `test.${mimeType.split("/")[1]}`;
|
|
348
|
+
|
|
349
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
350
|
+
const content = await file.text();
|
|
351
|
+
|
|
352
|
+
expect(file.type).toMatch(new RegExp(`^${mimeType.replace(/[+/]/g, "\\$&")}(;.*)?$`));
|
|
353
|
+
expect(content).toBe(expectedContent);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test.each([
|
|
357
|
+
["test.txt"],
|
|
358
|
+
["my-file.pdf"],
|
|
359
|
+
["image_01.jpg"],
|
|
360
|
+
["document with spaces.docx"],
|
|
361
|
+
["file.with.multiple.dots.txt"],
|
|
362
|
+
["🎉emoji-filename.txt"],
|
|
363
|
+
])("should preserve filename: %s", (filename) => {
|
|
364
|
+
const dataUrl = "data:text/plain;base64,dGVzdA==";
|
|
365
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
366
|
+
expect(file.name).toBe(filename);
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
describe("integration with File API", () => {
|
|
371
|
+
test("should work with FormData", () => {
|
|
372
|
+
const dataUrl = "data:text/plain;base64,SGVsbG8gV29ybGQ=";
|
|
373
|
+
const filename = "test.txt";
|
|
374
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
375
|
+
|
|
376
|
+
const formData = new FormData();
|
|
377
|
+
formData.append("file", file);
|
|
378
|
+
|
|
379
|
+
const retrievedFile = formData.get("file") as File;
|
|
380
|
+
expect(retrievedFile).toBeInstanceOf(File);
|
|
381
|
+
expect(retrievedFile.name).toBe(filename);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test("should be compatible with URL.createObjectURL", () => {
|
|
385
|
+
const dataUrl =
|
|
386
|
+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==";
|
|
387
|
+
const filename = "pixel.png";
|
|
388
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
389
|
+
|
|
390
|
+
const objectUrl = URL.createObjectURL(file);
|
|
391
|
+
|
|
392
|
+
expect(typeof objectUrl).toBe("string");
|
|
393
|
+
expect(objectUrl.startsWith("blob:")).toBe(true);
|
|
394
|
+
|
|
395
|
+
// Clean up
|
|
396
|
+
URL.revokeObjectURL(objectUrl);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
test("should work with file size limits", () => {
|
|
400
|
+
const dataUrl = "data:text/plain;base64,dGVzdA=="; // "test" = 4 bytes
|
|
401
|
+
const filename = "small.txt";
|
|
402
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
403
|
+
|
|
404
|
+
const maxSize = 10;
|
|
405
|
+
expect(file.size <= maxSize).toBe(true);
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
describe("performance considerations", () => {
|
|
410
|
+
test("should handle reasonably large files", () => {
|
|
411
|
+
// Create a larger base64 string (about 1KB)
|
|
412
|
+
const largeText = "A".repeat(1000);
|
|
413
|
+
const base64Data = btoa(largeText);
|
|
414
|
+
const dataUrl = `data:text/plain;base64,${base64Data}`;
|
|
415
|
+
const filename = "large.txt";
|
|
416
|
+
|
|
417
|
+
const startTime = performance.now();
|
|
418
|
+
const file = dataURLtoFile(dataUrl, filename);
|
|
419
|
+
const endTime = performance.now();
|
|
420
|
+
|
|
421
|
+
expect(file).toBeInstanceOf(File);
|
|
422
|
+
expect(file.size).toBe(1000);
|
|
423
|
+
expect(endTime - startTime).toBeLessThan(100); // Should be fast
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
test("should create independent files from same data", () => {
|
|
427
|
+
const dataUrl = "data:text/plain;base64,SGVsbG8=";
|
|
428
|
+
|
|
429
|
+
const file1 = dataURLtoFile(dataUrl, "file1.txt");
|
|
430
|
+
const file2 = dataURLtoFile(dataUrl, "file2.txt");
|
|
431
|
+
|
|
432
|
+
expect(file1).not.toBe(file2); // Different objects
|
|
433
|
+
expect(file1.name).toBe("file1.txt");
|
|
434
|
+
expect(file2.name).toBe("file2.txt");
|
|
435
|
+
expect(file1.size).toBe(file2.size);
|
|
436
|
+
expect(file1.type).toBe(file2.type);
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
describe("limitations and constraints", () => {
|
|
441
|
+
test("should document base64 requirement", () => {
|
|
442
|
+
// This function specifically requires base64 encoded data
|
|
443
|
+
const validDataUrl = "data:text/plain;base64,SGVsbG8=";
|
|
444
|
+
|
|
445
|
+
expect(() => {
|
|
446
|
+
dataURLtoFile(validDataUrl, "test.txt");
|
|
447
|
+
}).not.toThrow();
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
test("should handle mime type extraction correctly", () => {
|
|
451
|
+
// The function uses a regex to extract mime type between : and ;
|
|
452
|
+
const dataUrl = "data:custom/type;param=value;base64,dGVzdA==";
|
|
453
|
+
const file = dataURLtoFile(dataUrl, "test");
|
|
454
|
+
|
|
455
|
+
expect(file.type).toBe("custom/type");
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
test("should create Uint8Array internally", async () => {
|
|
459
|
+
const dataUrl = "data:application/octet-stream;base64,AAECAw==";
|
|
460
|
+
const file = dataURLtoFile(dataUrl, "test.bin");
|
|
461
|
+
|
|
462
|
+
const buffer = await file.arrayBuffer();
|
|
463
|
+
const uint8Array = new Uint8Array(buffer);
|
|
464
|
+
|
|
465
|
+
// Verify the internal Uint8Array was created correctly
|
|
466
|
+
expect(uint8Array[0]).toBe(0);
|
|
467
|
+
expect(uint8Array[1]).toBe(1);
|
|
468
|
+
expect(uint8Array[2]).toBe(2);
|
|
469
|
+
expect(uint8Array[3]).toBe(3);
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
});
|