@gjsify/fs 0.0.4 → 0.1.1
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 +31 -2
- package/lib/esm/callback.js +251 -15
- package/lib/esm/dirent.js +47 -6
- package/lib/esm/encoding.js +2 -3
- package/lib/esm/errors.js +13 -0
- package/lib/esm/file-handle.js +108 -66
- package/lib/esm/fs-watcher.js +44 -7
- package/lib/esm/index.js +140 -5
- package/lib/esm/promises.js +290 -69
- package/lib/esm/read-stream.js +82 -57
- package/lib/esm/stats.js +138 -18
- package/lib/esm/sync.js +293 -44
- package/lib/esm/write-stream.js +4 -4
- package/lib/types/callback.d.ts +233 -0
- package/lib/types/dirent.d.ts +77 -0
- package/lib/types/encoding.d.ts +6 -0
- package/lib/types/errors.d.ts +7 -0
- package/lib/types/file-handle.d.ts +367 -0
- package/lib/types/fs-watcher.d.ts +17 -0
- package/lib/types/index.d.ts +149 -0
- package/lib/types/promises.d.ts +158 -0
- package/lib/types/read-stream.d.ts +21 -0
- package/lib/types/stats.d.ts +67 -0
- package/lib/types/sync.d.ts +109 -0
- package/lib/types/types/encoding-option.d.ts +2 -0
- package/lib/types/types/file-read-options.d.ts +15 -0
- package/lib/types/types/file-read-result.d.ts +4 -0
- package/lib/types/types/flag-and-open-mode.d.ts +5 -0
- package/lib/types/types/index.d.ts +6 -0
- package/lib/types/types/open-flags.d.ts +1 -0
- package/lib/types/types/read-options.d.ts +5 -0
- package/lib/types/utils.d.ts +2 -0
- package/lib/types/write-stream.d.ts +45 -0
- package/package.json +22 -35
- package/src/callback.spec.ts +284 -30
- package/src/callback.ts +352 -39
- package/src/dirent.ts +56 -8
- package/src/encoding.ts +7 -2
- package/src/errors.spec.ts +389 -0
- package/src/errors.ts +19 -0
- package/src/extended.spec.ts +706 -0
- package/src/file-handle.spec.ts +104 -23
- package/src/file-handle.ts +147 -79
- package/src/fs-watcher.ts +55 -8
- package/src/index.ts +146 -2
- package/src/new-apis.spec.ts +505 -0
- package/src/promises.spec.ts +651 -11
- package/src/promises.ts +353 -81
- package/src/read-stream.ts +98 -74
- package/src/stat.spec.ts +22 -14
- package/src/stats.ts +176 -75
- package/src/streams.spec.ts +455 -0
- package/src/symlink.spec.ts +176 -26
- package/src/sync.spec.ts +204 -32
- package/src/sync.ts +363 -58
- package/src/test.mts +7 -2
- package/src/types/encoding-option.ts +1 -1
- package/src/types/flag-and-open-mode.ts +1 -1
- package/src/types/read-options.ts +2 -2
- package/src/utils.ts +2 -0
- package/src/write-stream.ts +9 -7
- package/tsconfig.json +23 -10
- package/tsconfig.tsbuildinfo +1 -0
- package/lib/cjs/callback.js +0 -112
- package/lib/cjs/dirent.js +0 -98
- package/lib/cjs/encoding.js +0 -34
- package/lib/cjs/file-handle.js +0 -444
- package/lib/cjs/fs-watcher.js +0 -50
- package/lib/cjs/index.js +0 -95
- package/lib/cjs/promises.js +0 -160
- package/lib/cjs/read-stream.js +0 -78
- package/lib/cjs/stats.js +0 -45
- package/lib/cjs/sync.js +0 -126
- package/lib/cjs/types/encoding-option.js +0 -0
- package/lib/cjs/types/file-read-options.js +0 -0
- package/lib/cjs/types/file-read-result.js +0 -0
- package/lib/cjs/types/flag-and-open-mode.js +0 -0
- package/lib/cjs/types/index.js +0 -6
- package/lib/cjs/types/open-flags.js +0 -0
- package/lib/cjs/types/read-options.js +0 -0
- package/lib/cjs/utils.js +0 -18
- package/lib/cjs/write-stream.js +0 -116
- package/test/watch.js +0 -1
- package/test.gjs.js +0 -35359
- package/test.gjs.js.map +0 -7
- package/test.gjs.mjs +0 -40534
- package/test.gjs.mjs.meta.json +0 -1
- package/test.node.js +0 -1479
- package/test.node.js.map +0 -7
- package/test.node.mjs +0 -710
- package/tsconfig.types.json +0 -8
package/src/promises.spec.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
// Ported from refs/node-test/parallel/test-fs-promises.js,
|
|
2
|
+
// test-fs-access.js, test-fs-copyfile.js
|
|
3
|
+
// Original: MIT license, Node.js contributors
|
|
1
4
|
import { describe, it, expect } from '@gjsify/unit';
|
|
2
|
-
import { promises, existsSync } from 'fs';
|
|
3
|
-
import { mkdir, readdir, mkdtemp, writeFile, rm, rmdir } from 'fs/promises';
|
|
4
|
-
import { join } from 'path';
|
|
5
|
-
import {
|
|
5
|
+
import { promises, existsSync, mkdtempSync, writeFileSync, rmdirSync, rmSync, constants as fsConstants } from 'node:fs';
|
|
6
|
+
import { mkdir, readdir, mkdtemp, writeFile, rm, rmdir, access, copyFile, rename, lstat, stat, unlink } from 'node:fs/promises';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { tmpdir } from 'node:os';
|
|
9
|
+
import { Buffer } from 'node:buffer';
|
|
6
10
|
|
|
7
11
|
export default async () => {
|
|
8
12
|
|
|
@@ -91,8 +95,8 @@ export default async () => {
|
|
|
91
95
|
expect(typeof promises.readFile).toBe("function");
|
|
92
96
|
});
|
|
93
97
|
|
|
94
|
-
await it('should
|
|
95
|
-
expect(promises.readFile('
|
|
98
|
+
await it('should return a promise', async () => {
|
|
99
|
+
expect(promises.readFile('package.json', 'utf-8') instanceof Promise).toBeTruthy();
|
|
96
100
|
});
|
|
97
101
|
|
|
98
102
|
await it('should return a Buffer if no encoding was specified', async () => {
|
|
@@ -101,13 +105,23 @@ export default async () => {
|
|
|
101
105
|
});
|
|
102
106
|
|
|
103
107
|
await it('should return a string when encoding is utf-8', async () => {
|
|
104
|
-
const
|
|
108
|
+
const dir = mkdtempSync('fs-prf-');
|
|
109
|
+
const filePath = join(dir, 'test.txt');
|
|
110
|
+
writeFileSync(filePath, 'Hello World');
|
|
111
|
+
const utf8Data = await promises.readFile(filePath, 'utf-8');
|
|
105
112
|
expect(typeof utf8Data === 'string').toBeTruthy();
|
|
113
|
+
rmSync(filePath);
|
|
114
|
+
rmdirSync(dir);
|
|
106
115
|
});
|
|
107
116
|
|
|
108
|
-
await it('should return
|
|
109
|
-
const
|
|
117
|
+
await it('should return the correct file content', async () => {
|
|
118
|
+
const dir = mkdtempSync('fs-prf-content-');
|
|
119
|
+
const filePath = join(dir, 'test.txt');
|
|
120
|
+
writeFileSync(filePath, 'Hello World');
|
|
121
|
+
const utf8Data = await promises.readFile(filePath, 'utf-8');
|
|
110
122
|
expect(utf8Data).toBe('Hello World');
|
|
123
|
+
rmSync(filePath);
|
|
124
|
+
rmdirSync(dir);
|
|
111
125
|
});
|
|
112
126
|
});
|
|
113
127
|
|
|
@@ -123,7 +137,156 @@ export default async () => {
|
|
|
123
137
|
});
|
|
124
138
|
});
|
|
125
139
|
|
|
126
|
-
|
|
140
|
+
await describe('fs.promises.access', async () => {
|
|
141
|
+
await it('should resolve for an existing file with F_OK', async () => {
|
|
142
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-acc-'));
|
|
143
|
+
const file = join(dir, 'exists.txt');
|
|
144
|
+
await writeFile(file, '');
|
|
145
|
+
await access(file, fsConstants.F_OK);
|
|
146
|
+
await rm(file);
|
|
147
|
+
await rmdir(dir);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
await it('should reject with ENOENT for non-existent file', async () => {
|
|
151
|
+
let threw = false;
|
|
152
|
+
try {
|
|
153
|
+
await access('/nonexistent/abc123/xyz.txt', fsConstants.F_OK);
|
|
154
|
+
} catch (e: unknown) {
|
|
155
|
+
threw = true;
|
|
156
|
+
expect((e as NodeJS.ErrnoException).code).toBe('ENOENT');
|
|
157
|
+
}
|
|
158
|
+
expect(threw).toBe(true);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
await it('should resolve for readable file with R_OK', async () => {
|
|
162
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-acc-r-'));
|
|
163
|
+
const file = join(dir, 'read.txt');
|
|
164
|
+
await writeFile(file, 'readable');
|
|
165
|
+
await access(file, fsConstants.R_OK);
|
|
166
|
+
await rm(file);
|
|
167
|
+
await rmdir(dir);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
await describe('fs.promises.copyFile', async () => {
|
|
172
|
+
await it('should copy a file', async () => {
|
|
173
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-cp-'));
|
|
174
|
+
const src = join(dir, 'src.txt');
|
|
175
|
+
const dst = join(dir, 'dst.txt');
|
|
176
|
+
await writeFile(src, 'copy content');
|
|
177
|
+
await copyFile(src, dst);
|
|
178
|
+
const content = await promises.readFile(dst, { encoding: 'utf8' });
|
|
179
|
+
expect(content).toBe('copy content');
|
|
180
|
+
await rm(src);
|
|
181
|
+
await rm(dst);
|
|
182
|
+
await rmdir(dir);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
await it('should throw ENOENT when source does not exist', async () => {
|
|
186
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-cp-err-'));
|
|
187
|
+
const dst = join(dir, 'dst.txt');
|
|
188
|
+
let threw = false;
|
|
189
|
+
try {
|
|
190
|
+
await copyFile('/nonexistent/xyz123.txt', dst);
|
|
191
|
+
} catch (e: unknown) {
|
|
192
|
+
threw = true;
|
|
193
|
+
expect((e as NodeJS.ErrnoException).code).toBe('ENOENT');
|
|
194
|
+
}
|
|
195
|
+
expect(threw).toBe(true);
|
|
196
|
+
await rmdir(dir);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
await it('should overwrite destination by default', async () => {
|
|
200
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-cp-ow-'));
|
|
201
|
+
const src = join(dir, 'src.txt');
|
|
202
|
+
const dst = join(dir, 'dst.txt');
|
|
203
|
+
await writeFile(src, 'new content');
|
|
204
|
+
await writeFile(dst, 'old content');
|
|
205
|
+
await copyFile(src, dst);
|
|
206
|
+
const content = await promises.readFile(dst, { encoding: 'utf8' });
|
|
207
|
+
expect(content).toBe('new content');
|
|
208
|
+
await rm(src);
|
|
209
|
+
await rm(dst);
|
|
210
|
+
await rmdir(dir);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
await describe('fs.promises.rename', async () => {
|
|
215
|
+
await it('should rename a file', async () => {
|
|
216
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-ren-'));
|
|
217
|
+
const src = join(dir, 'old.txt');
|
|
218
|
+
const dst = join(dir, 'new.txt');
|
|
219
|
+
await writeFile(src, 'rename content');
|
|
220
|
+
await rename(src, dst);
|
|
221
|
+
expect(existsSync(src)).toBe(false);
|
|
222
|
+
expect(existsSync(dst)).toBe(true);
|
|
223
|
+
const content = await promises.readFile(dst, { encoding: 'utf8' });
|
|
224
|
+
expect(content).toBe('rename content');
|
|
225
|
+
await rm(dst);
|
|
226
|
+
await rmdir(dir);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
await it('should throw ENOENT when source does not exist', async () => {
|
|
230
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-ren-err-'));
|
|
231
|
+
let threw = false;
|
|
232
|
+
try {
|
|
233
|
+
await rename(join(dir, 'nonexistent.txt'), join(dir, 'dst.txt'));
|
|
234
|
+
} catch (e: unknown) {
|
|
235
|
+
threw = true;
|
|
236
|
+
expect((e as NodeJS.ErrnoException).code).toBe('ENOENT');
|
|
237
|
+
}
|
|
238
|
+
expect(threw).toBe(true);
|
|
239
|
+
await rmdir(dir);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
await describe('fs.promises.lstat', async () => {
|
|
244
|
+
await it('should return stat for a regular file', async () => {
|
|
245
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-lst-'));
|
|
246
|
+
const file = join(dir, 'file.txt');
|
|
247
|
+
await writeFile(file, 'lstat test');
|
|
248
|
+
const s = await lstat(file);
|
|
249
|
+
expect(s.isFile()).toBe(true);
|
|
250
|
+
expect(s.isDirectory()).toBe(false);
|
|
251
|
+
expect(s.isSymbolicLink()).toBe(false);
|
|
252
|
+
await rm(file);
|
|
253
|
+
await rmdir(dir);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
await it('should return stat for a directory', async () => {
|
|
257
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-lst-d-'));
|
|
258
|
+
const s = await lstat(dir);
|
|
259
|
+
expect(s.isDirectory()).toBe(true);
|
|
260
|
+
expect(s.isFile()).toBe(false);
|
|
261
|
+
await rmdir(dir);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
await it('should throw ENOENT for non-existent path', async () => {
|
|
265
|
+
let threw = false;
|
|
266
|
+
try {
|
|
267
|
+
await lstat('/nonexistent/abc/xyz123.txt');
|
|
268
|
+
} catch (e: unknown) {
|
|
269
|
+
threw = true;
|
|
270
|
+
expect((e as NodeJS.ErrnoException).code).toBe('ENOENT');
|
|
271
|
+
}
|
|
272
|
+
expect(threw).toBe(true);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
await describe('fs.promises.writeFile error handling', async () => {
|
|
277
|
+
await it('should throw ENOENT when parent directory does not exist', async () => {
|
|
278
|
+
let threw = false;
|
|
279
|
+
try {
|
|
280
|
+
await writeFile('/nonexistent/subdir/abc123.txt', 'data');
|
|
281
|
+
} catch (e: unknown) {
|
|
282
|
+
threw = true;
|
|
283
|
+
expect((e as NodeJS.ErrnoException).code).toBe('ENOENT');
|
|
284
|
+
}
|
|
285
|
+
expect(threw).toBe(true);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
await describe('fs.promises.rm', async () => {
|
|
127
290
|
await it('should be a function', async () => {
|
|
128
291
|
expect(typeof promises.rm).toBe("function");
|
|
129
292
|
});
|
|
@@ -138,7 +301,7 @@ export default async () => {
|
|
|
138
301
|
expect(existsSync(txt1)).toBeTruthy();
|
|
139
302
|
await promises.rm(txt1);
|
|
140
303
|
expect(existsSync(txt1)).toBeFalsy();
|
|
141
|
-
|
|
304
|
+
|
|
142
305
|
// Clear
|
|
143
306
|
await promises.rmdir(dir);
|
|
144
307
|
});
|
|
@@ -169,4 +332,481 @@ export default async () => {
|
|
|
169
332
|
expect(existsSync(dir)).toBeFalsy();
|
|
170
333
|
});
|
|
171
334
|
});
|
|
335
|
+
|
|
336
|
+
// --- New tests below ---
|
|
337
|
+
|
|
338
|
+
await describe('fs.promises.writeFile + readFile round-trip', async () => {
|
|
339
|
+
await it('should write and read back a string correctly', async () => {
|
|
340
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-prt-'));
|
|
341
|
+
const file = join(dir, 'roundtrip.txt');
|
|
342
|
+
const content = 'Hello, round-trip test!';
|
|
343
|
+
await writeFile(file, content);
|
|
344
|
+
const result = await promises.readFile(file, 'utf-8');
|
|
345
|
+
expect(result).toBe(content);
|
|
346
|
+
await rm(file);
|
|
347
|
+
await rmdir(dir);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
await it('should write and read back a Buffer correctly', async () => {
|
|
351
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-prt-buf-'));
|
|
352
|
+
const file = join(dir, 'roundtrip-buf.bin');
|
|
353
|
+
const data = Buffer.from([0x00, 0x01, 0x02, 0xff]);
|
|
354
|
+
await writeFile(file, data);
|
|
355
|
+
const result = await promises.readFile(file);
|
|
356
|
+
expect(result instanceof Buffer).toBeTruthy();
|
|
357
|
+
expect(result[0]).toBe(0x00);
|
|
358
|
+
expect(result[1]).toBe(0x01);
|
|
359
|
+
expect(result[2]).toBe(0x02);
|
|
360
|
+
expect(result[3]).toBe(0xff);
|
|
361
|
+
expect(result.length).toBe(4);
|
|
362
|
+
await rm(file);
|
|
363
|
+
await rmdir(dir);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
await it('should write an empty file and read it back', async () => {
|
|
367
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-prt-empty-'));
|
|
368
|
+
const file = join(dir, 'empty.txt');
|
|
369
|
+
await writeFile(file, '');
|
|
370
|
+
const result = await promises.readFile(file, 'utf-8');
|
|
371
|
+
expect(result).toBe('');
|
|
372
|
+
await rm(file);
|
|
373
|
+
await rmdir(dir);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
await it('should write unicode content and read it back', async () => {
|
|
377
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-prt-uni-'));
|
|
378
|
+
const file = join(dir, 'unicode.txt');
|
|
379
|
+
const content = 'Hej! Caf\u00e9 \u2603 \ud83d\ude00';
|
|
380
|
+
await writeFile(file, content);
|
|
381
|
+
const result = await promises.readFile(file, 'utf-8');
|
|
382
|
+
expect(result).toBe(content);
|
|
383
|
+
await rm(file);
|
|
384
|
+
await rmdir(dir);
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
await describe('fs.promises.mkdir / rmdir', async () => {
|
|
389
|
+
await it('should create and remove a directory', async () => {
|
|
390
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-pmkdir-'));
|
|
391
|
+
const sub = join(dir, 'subdir');
|
|
392
|
+
await mkdir(sub);
|
|
393
|
+
expect(existsSync(sub)).toBe(true);
|
|
394
|
+
await rmdir(sub);
|
|
395
|
+
expect(existsSync(sub)).toBe(false);
|
|
396
|
+
await rmdir(dir);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
await it('should create nested directories with recursive option', async () => {
|
|
400
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-pmkdir-rec-'));
|
|
401
|
+
const nested = join(dir, 'a', 'b', 'c');
|
|
402
|
+
await mkdir(nested, { recursive: true });
|
|
403
|
+
expect(existsSync(nested)).toBe(true);
|
|
404
|
+
await promises.rm(dir, { recursive: true });
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
await it('should not throw when recursive mkdir on existing directory', async () => {
|
|
408
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-pmkdir-exist-'));
|
|
409
|
+
// Should not throw
|
|
410
|
+
await mkdir(dir, { recursive: true });
|
|
411
|
+
await rmdir(dir);
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
await describe('fs.promises.stat', async () => {
|
|
416
|
+
await it('should return stat for a file with correct size', async () => {
|
|
417
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-pstat-'));
|
|
418
|
+
const file = join(dir, 'sized.txt');
|
|
419
|
+
const content = 'abcdef'; // 6 bytes in UTF-8
|
|
420
|
+
await writeFile(file, content);
|
|
421
|
+
const s = await stat(file);
|
|
422
|
+
expect(s.isFile()).toBe(true);
|
|
423
|
+
expect(s.isDirectory()).toBe(false);
|
|
424
|
+
expect(s.size).toBe(6);
|
|
425
|
+
await rm(file);
|
|
426
|
+
await rmdir(dir);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
await it('should return stat for a directory', async () => {
|
|
430
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-pstat-d-'));
|
|
431
|
+
const s = await stat(dir);
|
|
432
|
+
expect(s.isDirectory()).toBe(true);
|
|
433
|
+
expect(s.isFile()).toBe(false);
|
|
434
|
+
await rmdir(dir);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
await it('should reject with ENOENT for non-existent file', async () => {
|
|
438
|
+
let threw = false;
|
|
439
|
+
try {
|
|
440
|
+
await stat('/nonexistent/xyz789/no-file.txt');
|
|
441
|
+
} catch (e: unknown) {
|
|
442
|
+
threw = true;
|
|
443
|
+
expect((e as NodeJS.ErrnoException).code).toBe('ENOENT');
|
|
444
|
+
}
|
|
445
|
+
expect(threw).toBe(true);
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
await describe('fs.promises.access additional', async () => {
|
|
450
|
+
await it('should reject with EACCES or ENOENT for W_OK on read-only path', async () => {
|
|
451
|
+
// /etc/hosts typically is not writable by normal users
|
|
452
|
+
let threw = false;
|
|
453
|
+
try {
|
|
454
|
+
await access('/etc/hosts', fsConstants.W_OK);
|
|
455
|
+
} catch (e: unknown) {
|
|
456
|
+
threw = true;
|
|
457
|
+
const code = (e as NodeJS.ErrnoException).code;
|
|
458
|
+
// Either EACCES (permission denied) or ENOENT depending on system
|
|
459
|
+
expect(code === 'EACCES' || code === 'ENOENT').toBe(true);
|
|
460
|
+
}
|
|
461
|
+
expect(threw).toBe(true);
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
await describe('fs.promises.rename additional', async () => {
|
|
466
|
+
await it('should preserve file content after rename', async () => {
|
|
467
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-pren-content-'));
|
|
468
|
+
const src = join(dir, 'before.txt');
|
|
469
|
+
const dst = join(dir, 'after.txt');
|
|
470
|
+
await writeFile(src, 'content to preserve');
|
|
471
|
+
await rename(src, dst);
|
|
472
|
+
const content = await promises.readFile(dst, 'utf-8');
|
|
473
|
+
expect(content).toBe('content to preserve');
|
|
474
|
+
expect(existsSync(src)).toBe(false);
|
|
475
|
+
await rm(dst);
|
|
476
|
+
await rmdir(dir);
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
await it('should overwrite destination if it already exists', async () => {
|
|
480
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-pren-ow-'));
|
|
481
|
+
const src = join(dir, 'src.txt');
|
|
482
|
+
const dst = join(dir, 'dst.txt');
|
|
483
|
+
await writeFile(src, 'new');
|
|
484
|
+
await writeFile(dst, 'old');
|
|
485
|
+
await rename(src, dst);
|
|
486
|
+
const content = await promises.readFile(dst, 'utf-8');
|
|
487
|
+
expect(content).toBe('new');
|
|
488
|
+
expect(existsSync(src)).toBe(false);
|
|
489
|
+
await rm(dst);
|
|
490
|
+
await rmdir(dir);
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
await describe('fs.promises.copyFile additional', async () => {
|
|
495
|
+
await it('should not modify original file after copy', async () => {
|
|
496
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-pcp-orig-'));
|
|
497
|
+
const src = join(dir, 'original.txt');
|
|
498
|
+
const dst = join(dir, 'copied.txt');
|
|
499
|
+
await writeFile(src, 'original');
|
|
500
|
+
await copyFile(src, dst);
|
|
501
|
+
const srcContent = await promises.readFile(src, 'utf-8');
|
|
502
|
+
const dstContent = await promises.readFile(dst, 'utf-8');
|
|
503
|
+
expect(srcContent).toBe('original');
|
|
504
|
+
expect(dstContent).toBe('original');
|
|
505
|
+
await rm(src);
|
|
506
|
+
await rm(dst);
|
|
507
|
+
await rmdir(dir);
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
await describe('fs.promises.unlink', async () => {
|
|
512
|
+
await it('should remove a file', async () => {
|
|
513
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-punlink-'));
|
|
514
|
+
const file = join(dir, 'to-delete.txt');
|
|
515
|
+
await writeFile(file, 'delete me');
|
|
516
|
+
expect(existsSync(file)).toBe(true);
|
|
517
|
+
await unlink(file);
|
|
518
|
+
expect(existsSync(file)).toBe(false);
|
|
519
|
+
await rmdir(dir);
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
await it('should throw ENOENT for non-existent file', async () => {
|
|
523
|
+
let threw = false;
|
|
524
|
+
try {
|
|
525
|
+
await unlink('/nonexistent/abc/delete-me.txt');
|
|
526
|
+
} catch (e: unknown) {
|
|
527
|
+
threw = true;
|
|
528
|
+
expect((e as NodeJS.ErrnoException).code).toBe('ENOENT');
|
|
529
|
+
}
|
|
530
|
+
expect(threw).toBe(true);
|
|
531
|
+
});
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
await describe('fs.promises.readdir additional', async () => {
|
|
535
|
+
await it('should return sorted entries for multiple files', async () => {
|
|
536
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-preaddir-sort-'));
|
|
537
|
+
await writeFile(join(dir, 'c.txt'), '');
|
|
538
|
+
await writeFile(join(dir, 'a.txt'), '');
|
|
539
|
+
await writeFile(join(dir, 'b.txt'), '');
|
|
540
|
+
const files = await readdir(dir);
|
|
541
|
+
// readdir does not guarantee order, but should return all 3 entries
|
|
542
|
+
expect(files.length).toBe(3);
|
|
543
|
+
expect(files).toContain('a.txt');
|
|
544
|
+
expect(files).toContain('b.txt');
|
|
545
|
+
expect(files).toContain('c.txt');
|
|
546
|
+
await promises.rm(dir, { recursive: true });
|
|
547
|
+
});
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
await describe('fs.promises.appendFile', async () => {
|
|
551
|
+
await it('should append data to an existing file', async () => {
|
|
552
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-pappend-'));
|
|
553
|
+
const file = join(dir, 'append.txt');
|
|
554
|
+
await writeFile(file, 'hello');
|
|
555
|
+
await promises.appendFile(file, ' world');
|
|
556
|
+
const content = await promises.readFile(file, 'utf-8');
|
|
557
|
+
expect(content).toBe('hello world');
|
|
558
|
+
await rm(file);
|
|
559
|
+
await rmdir(dir);
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
await it('should create a new file if it does not exist', async () => {
|
|
563
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-pappend-new-'));
|
|
564
|
+
const file = join(dir, 'new-append.txt');
|
|
565
|
+
await promises.appendFile(file, 'created');
|
|
566
|
+
expect(existsSync(file)).toBe(true);
|
|
567
|
+
const content = await promises.readFile(file, 'utf-8');
|
|
568
|
+
expect(content).toBe('created');
|
|
569
|
+
await rm(file);
|
|
570
|
+
await rmdir(dir);
|
|
571
|
+
});
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
await describe('fs.promises.chmod', async () => {
|
|
575
|
+
await it('should change file mode', async () => {
|
|
576
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-pchmod-'));
|
|
577
|
+
const file = join(dir, 'chmod.txt');
|
|
578
|
+
await writeFile(file, 'chmod test');
|
|
579
|
+
await promises.chmod(file, 0o444);
|
|
580
|
+
const s = await stat(file);
|
|
581
|
+
// Check that the permission bits match (mask with 0o777 for portable comparison)
|
|
582
|
+
expect(s.mode & 0o777).toBe(0o444);
|
|
583
|
+
// Restore write permission for cleanup
|
|
584
|
+
await promises.chmod(file, 0o644);
|
|
585
|
+
await rm(file);
|
|
586
|
+
await rmdir(dir);
|
|
587
|
+
});
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
await describe('fs.promises.mkdtemp additional', async () => {
|
|
591
|
+
await it('should create a directory with the given prefix', async () => {
|
|
592
|
+
const prefix = join(tmpdir(), 'fs-pmkdtemp-test-');
|
|
593
|
+
const dir = await promises.mkdtemp(prefix);
|
|
594
|
+
expect(existsSync(dir)).toBe(true);
|
|
595
|
+
expect(dir.startsWith(prefix)).toBe(true);
|
|
596
|
+
// The random suffix should add characters
|
|
597
|
+
expect(dir.length).toBeGreaterThan(prefix.length);
|
|
598
|
+
await rmdir(dir);
|
|
599
|
+
});
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
await describe('fs.promises.realpath', async () => {
|
|
603
|
+
await it('should resolve a simple path', async () => {
|
|
604
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-prealpath-'));
|
|
605
|
+
const file = join(dir, 'real.txt');
|
|
606
|
+
await writeFile(file, 'data');
|
|
607
|
+
const resolved = await promises.realpath(file);
|
|
608
|
+
// Should be an absolute path
|
|
609
|
+
expect(resolved.startsWith('/')).toBe(true);
|
|
610
|
+
// Should end with the file name
|
|
611
|
+
expect(resolved.endsWith('real.txt')).toBe(true);
|
|
612
|
+
await rm(file);
|
|
613
|
+
await rmdir(dir);
|
|
614
|
+
});
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
await describe('fs.promises.truncate', async () => {
|
|
618
|
+
await it('should truncate a file to a specified length', async () => {
|
|
619
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-ptrunc-'));
|
|
620
|
+
const file = join(dir, 'trunc.txt');
|
|
621
|
+
await writeFile(file, 'hello world');
|
|
622
|
+
await promises.truncate(file, 5);
|
|
623
|
+
const content = await promises.readFile(file, 'utf-8');
|
|
624
|
+
expect(content).toBe('hello');
|
|
625
|
+
await rm(file);
|
|
626
|
+
await rmdir(dir);
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
await it('should truncate a file to zero length when no length specified', async () => {
|
|
630
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-ptrunc0-'));
|
|
631
|
+
const file = join(dir, 'trunc0.txt');
|
|
632
|
+
await writeFile(file, 'data to remove');
|
|
633
|
+
await promises.truncate(file);
|
|
634
|
+
const content = await promises.readFile(file, 'utf-8');
|
|
635
|
+
expect(content).toBe('');
|
|
636
|
+
await rm(file);
|
|
637
|
+
await rmdir(dir);
|
|
638
|
+
});
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
await describe('fs.promises error cases', async () => {
|
|
642
|
+
await it('readFile should reject for non-existent file', async () => {
|
|
643
|
+
let threw = false;
|
|
644
|
+
try {
|
|
645
|
+
await promises.readFile('/nonexistent/path/xyz987.txt');
|
|
646
|
+
} catch (e: unknown) {
|
|
647
|
+
threw = true;
|
|
648
|
+
expect((e as NodeJS.ErrnoException).code).toBe('ENOENT');
|
|
649
|
+
}
|
|
650
|
+
expect(threw).toBe(true);
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
await it('readdir should reject for non-existent directory', async () => {
|
|
654
|
+
let threw = false;
|
|
655
|
+
try {
|
|
656
|
+
await readdir('/nonexistent/xyz654/dir');
|
|
657
|
+
} catch (e: unknown) {
|
|
658
|
+
threw = true;
|
|
659
|
+
expect((e as NodeJS.ErrnoException).code).toBe('ENOENT');
|
|
660
|
+
}
|
|
661
|
+
expect(threw).toBe(true);
|
|
662
|
+
});
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
await describe('fs.promises return types', async () => {
|
|
666
|
+
await it('readFile should return a Promise', async () => {
|
|
667
|
+
const p = promises.readFile('/etc/hosts');
|
|
668
|
+
expect(p instanceof Promise).toBe(true);
|
|
669
|
+
await p; // consume
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
await it('writeFile should return a Promise', async () => {
|
|
673
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-pret-wf-'));
|
|
674
|
+
const file = join(dir, 'ret.txt');
|
|
675
|
+
const p = writeFile(file, 'test');
|
|
676
|
+
expect(p instanceof Promise).toBe(true);
|
|
677
|
+
await p;
|
|
678
|
+
await rm(file);
|
|
679
|
+
await rmdir(dir);
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
await it('stat should return a Promise', async () => {
|
|
683
|
+
const p = stat('/tmp');
|
|
684
|
+
expect(p instanceof Promise).toBe(true);
|
|
685
|
+
await p;
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
await it('mkdir should return a Promise', async () => {
|
|
689
|
+
const dir = join(tmpdir(), 'fs-pret-mkdir-' + Date.now());
|
|
690
|
+
const p = mkdir(dir);
|
|
691
|
+
expect(p instanceof Promise).toBe(true);
|
|
692
|
+
await p;
|
|
693
|
+
await rmdir(dir);
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
await it('access should return a Promise', async () => {
|
|
697
|
+
const p = access('/tmp', fsConstants.F_OK);
|
|
698
|
+
expect(p instanceof Promise).toBe(true);
|
|
699
|
+
await p;
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
await it('unlink should return a Promise', async () => {
|
|
703
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-pret-ul-'));
|
|
704
|
+
const file = join(dir, 'unlinkret.txt');
|
|
705
|
+
await writeFile(file, '');
|
|
706
|
+
const p = unlink(file);
|
|
707
|
+
expect(p instanceof Promise).toBe(true);
|
|
708
|
+
await p;
|
|
709
|
+
await rmdir(dir);
|
|
710
|
+
});
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
await describe('fs.promises.mkdir recursive return value', async () => {
|
|
714
|
+
await it('should return the first directory created', async () => {
|
|
715
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-pmkdir-retval-'));
|
|
716
|
+
const nested = join(dir, 'x', 'y', 'z');
|
|
717
|
+
const result = await mkdir(nested, { recursive: true });
|
|
718
|
+
// Should return the first directory created (x)
|
|
719
|
+
expect(typeof result).toBe('string');
|
|
720
|
+
expect(result).toBe(join(dir, 'x'));
|
|
721
|
+
expect(existsSync(nested)).toBe(true);
|
|
722
|
+
await promises.rm(dir, { recursive: true });
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
await it('should return undefined when directory already exists', async () => {
|
|
726
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-pmkdir-exist-'));
|
|
727
|
+
const result = await mkdir(dir, { recursive: true });
|
|
728
|
+
expect(result).toBeUndefined();
|
|
729
|
+
await rmdir(dir);
|
|
730
|
+
});
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
await describe('fs.promises.rmdir error handling', async () => {
|
|
734
|
+
await it('should throw ENOTEMPTY when directory is not empty', async () => {
|
|
735
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-prmdir-notempty-'));
|
|
736
|
+
const file = join(dir, 'file.txt');
|
|
737
|
+
await writeFile(file, 'data');
|
|
738
|
+
let threw = false;
|
|
739
|
+
try {
|
|
740
|
+
await rmdir(dir);
|
|
741
|
+
} catch (e: unknown) {
|
|
742
|
+
threw = true;
|
|
743
|
+
expect((e as NodeJS.ErrnoException).code).toBe('ENOTEMPTY');
|
|
744
|
+
}
|
|
745
|
+
expect(threw).toBe(true);
|
|
746
|
+
await promises.rm(dir, { recursive: true });
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
await it('should throw ENOENT when directory does not exist', async () => {
|
|
750
|
+
let threw = false;
|
|
751
|
+
try {
|
|
752
|
+
await rmdir('/nonexistent/xyz789/no-dir');
|
|
753
|
+
} catch (e: unknown) {
|
|
754
|
+
threw = true;
|
|
755
|
+
expect((e as NodeJS.ErrnoException).code).toBe('ENOENT');
|
|
756
|
+
}
|
|
757
|
+
expect(threw).toBe(true);
|
|
758
|
+
});
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
await describe('fs.promises.unlink error handling', async () => {
|
|
762
|
+
await it('should throw ENOENT for non-existent file', async () => {
|
|
763
|
+
let threw = false;
|
|
764
|
+
try {
|
|
765
|
+
await unlink('/nonexistent/xyz789/file.txt');
|
|
766
|
+
} catch (e: unknown) {
|
|
767
|
+
threw = true;
|
|
768
|
+
expect((e as NodeJS.ErrnoException).code).toBe('ENOENT');
|
|
769
|
+
}
|
|
770
|
+
expect(threw).toBe(true);
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
await it('should actually delete the file asynchronously', async () => {
|
|
774
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-punlink-async-'));
|
|
775
|
+
const file = join(dir, 'async-delete.txt');
|
|
776
|
+
await writeFile(file, 'data');
|
|
777
|
+
expect(existsSync(file)).toBe(true);
|
|
778
|
+
await unlink(file);
|
|
779
|
+
expect(existsSync(file)).toBe(false);
|
|
780
|
+
await rmdir(dir);
|
|
781
|
+
});
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
await describe('fs.promises.rm error handling', async () => {
|
|
785
|
+
await it('should throw when removing non-empty dir without recursive', async () => {
|
|
786
|
+
const dir = await mkdtemp(join(tmpdir(), 'fs-prm-notempty-'));
|
|
787
|
+
await writeFile(join(dir, 'file.txt'), 'data');
|
|
788
|
+
let threw = false;
|
|
789
|
+
try {
|
|
790
|
+
await promises.rm(dir, { recursive: false });
|
|
791
|
+
} catch (e: unknown) {
|
|
792
|
+
threw = true;
|
|
793
|
+
// Node.js throws ERR_FS_EISDIR, GJS throws ENOTEMPTY — both are correct
|
|
794
|
+
const code = (e as NodeJS.ErrnoException).code;
|
|
795
|
+
expect(code === 'ENOTEMPTY' || code === 'ERR_FS_EISDIR').toBe(true);
|
|
796
|
+
}
|
|
797
|
+
expect(threw).toBe(true);
|
|
798
|
+
await promises.rm(dir, { recursive: true });
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
await it('should not throw with force: true for non-existent path', async () => {
|
|
802
|
+
const path = join(tmpdir(), 'fs-prm-force-nonexistent-' + Date.now());
|
|
803
|
+
let threw = false;
|
|
804
|
+
try {
|
|
805
|
+
await promises.rm(path, { force: true });
|
|
806
|
+
} catch {
|
|
807
|
+
threw = true;
|
|
808
|
+
}
|
|
809
|
+
expect(threw).toBe(false);
|
|
810
|
+
});
|
|
811
|
+
});
|
|
172
812
|
}
|