File
Blob: src/workerd/api/tests/webfs-test.js
| 1 | // Copyright (c) 2025 Cloudflare, Inc. |
| 2 | // Licensed under the Apache 2.0 license found in the LICENSE file or at: |
| 3 | // https://opensource.org/licenses/Apache-2.0 |
| 4 | import { ok, strictEqual, deepStrictEqual, rejects } from 'node:assert'; |
| 5 | |
| 6 | ok(navigator.storage instanceof StorageManager); |
| 7 | |
| 8 | const dir = await navigator.storage.getDirectory(); |
| 9 | ok(dir instanceof FileSystemDirectoryHandle); |
| 10 | strictEqual(dir.name, ''); |
| 11 | |
| 12 | const bundle = await dir.getDirectoryHandle('bundle'); |
| 13 | const temp = await dir.getDirectoryHandle('tmp'); |
| 14 | |
| 15 | strictEqual(bundle.name, 'bundle'); |
| 16 | strictEqual(temp.name, 'tmp'); |
| 17 | |
| 18 | let count = 0; |
| 19 | for await (const node of dir) { |
| 20 | // The node is an Array of [name, handle] |
| 21 | strictEqual(typeof node[0], 'string'); |
| 22 | ok(node[1] instanceof FileSystemDirectoryHandle); |
| 23 | count++; |
| 24 | } |
| 25 | strictEqual(count, 3); |
| 26 | |
| 27 | const names = await Array.fromAsync(dir.keys()); |
| 28 | deepStrictEqual(names, ['bundle', 'tmp', 'dev']); |
| 29 | |
| 30 | export const webfsTest = { |
| 31 | async test() { |
| 32 | const file = await temp.getFileHandle('foo.txt', { create: true }); |
| 33 | ok(file instanceof FileSystemFileHandle); |
| 34 | const writable = await file.createWritable(); |
| 35 | ok(writable instanceof FileSystemWritableFileStream); |
| 36 | const enc = new TextEncoder(); |
| 37 | await writable.write(enc.encode('hello world')); |
| 38 | |
| 39 | // The original file contents remain unchanged until the writable is closed. |
| 40 | let f = await file.getFile(); |
| 41 | strictEqual(await f.text(), ''); |
| 42 | await writable.close(); |
| 43 | |
| 44 | // Once closed, however, the file contents are updated. |
| 45 | f = await file.getFile(); |
| 46 | strictEqual(await f.text(), 'hello world'); |
| 47 | |
| 48 | const file2 = await temp.getFileHandle('foo.txt'); |
| 49 | ok(file2 instanceof FileSystemFileHandle); |
| 50 | const fileStream = await file2.getFile(); |
| 51 | ok(fileStream instanceof File); |
| 52 | const text = await fileStream.text(); |
| 53 | strictEqual(text, 'hello world'); |
| 54 | |
| 55 | await rejects(temp.getFileHandle('does-not-exist.txt', { create: false }), { |
| 56 | message: 'Not found', |
| 57 | }); |
| 58 | }, |
| 59 | }; |
| 60 | |
| 61 | // TODO(node-fs): Rework this test now that createSyncAccessHandle has been removed. |
| 62 | // We can still test this using the the stream API but it needs to be structured |
| 63 | // a little differently. |
| 64 | // export const deviceTest = { |
| 65 | // async test() { |
| 66 | // // Test /dev/null |
| 67 | // const dev = await dir.getDirectoryHandle('dev'); |
| 68 | // const devNull = await dev.getFileHandle('null'); |
| 69 | // ok(devNull instanceof FileSystemFileHandle); |
| 70 | // const sync = await devNull.createSyncAccessHandle(); |
| 71 | // const enc = new TextEncoder(); |
| 72 | // strictEqual(sync.getSize(), 0); |
| 73 | // sync.write(enc.encode('hello world')); |
| 74 | // strictEqual(sync.getSize(), 0); |
| 75 | // strictEqual(sync.read(new Uint8Array(11)), 0); |
| 76 | |
| 77 | // // Test /dev/zero |
| 78 | // const devZero = await dev.getFileHandle('zero'); |
| 79 | // ok(devZero instanceof FileSystemFileHandle); |
| 80 | // const syncZero = await devZero.createSyncAccessHandle(); |
| 81 | // strictEqual(syncZero.getSize(), 0); |
| 82 | // syncZero.write(enc.encode('hello world')); |
| 83 | // strictEqual(syncZero.getSize(), 0); |
| 84 | // const u8 = new Buffer.from([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]); |
| 85 | // const zeroes = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); |
| 86 | // strictEqual(syncZero.read(u8), u8.byteLength); |
| 87 | // deepStrictEqual(u8, zeroes); |
| 88 | |
| 89 | // // Test /dev/full |
| 90 | // const devFull = await dev.getFileHandle('full'); |
| 91 | // ok(devFull instanceof FileSystemFileHandle); |
| 92 | // const syncFull = await devFull.createSyncAccessHandle(); |
| 93 | // strictEqual(syncFull.getSize(), 0); |
| 94 | // throws(() => syncFull.write(enc.encode('hello world')), { |
| 95 | // message: 'Operation not permitted', |
| 96 | // name: 'NotAllowedError', |
| 97 | // }); |
| 98 | // strictEqual(syncFull.getSize(), 0); |
| 99 | // const u8Full = new Buffer.from([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]); |
| 100 | // strictEqual(syncFull.read(u8Full), 11); |
| 101 | // deepStrictEqual(u8Full, zeroes); |
| 102 | |
| 103 | // // Test /dev/random |
| 104 | // const devRandom = await dev.getFileHandle('random'); |
| 105 | // ok(devRandom instanceof FileSystemFileHandle); |
| 106 | // const syncRandom = await devRandom.createSyncAccessHandle(); |
| 107 | // strictEqual(syncRandom.getSize(), 0); |
| 108 | // throws(() => syncRandom.write(enc.encode('hello world')), { |
| 109 | // message: 'Operation not permitted', |
| 110 | // name: 'NotAllowedError', |
| 111 | // }); |
| 112 | // strictEqual(syncRandom.getSize(), 0); |
| 113 | // const u8Random = new Buffer.from([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]); |
| 114 | // const check = new Buffer.from([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]); |
| 115 | // strictEqual(syncRandom.read(u8Random), 11); |
| 116 | // notDeepStrictEqual(u8Random, check); |
| 117 | // }, |
| 118 | // }; |
| 119 | |
| 120 | export const asyncIteratorTest = { |
| 121 | async test() { |
| 122 | const dir = await navigator.storage.getDirectory(); |
| 123 | |
| 124 | let count = 0; |
| 125 | for await (const node of dir) { |
| 126 | // The node is an Array of [name, handle] |
| 127 | ok(node[1] instanceof FileSystemDirectoryHandle); |
| 128 | count++; |
| 129 | } |
| 130 | strictEqual(count, 3); |
| 131 | |
| 132 | const names = await Array.fromAsync(dir.keys()); |
| 133 | deepStrictEqual(names, ['bundle', 'tmp', 'dev']); |
| 134 | }, |
| 135 | }; |