From b4a86c9a7d8efa1ef73ee6a6f17217cd2296890b Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sat, 21 Aug 2021 17:13:42 +0200 Subject: [PATCH] fs: add docs and tests for `AsyncIterable` support in `fh.writeFile` Refs: https://github.com/nodejs/node/pull/37490 --- doc/api/fs.md | 8 +- .../test-fs-promises-file-handle-writeFile.js | 127 +++++++++++++++++- 2 files changed, 130 insertions(+), 5 deletions(-) diff --git a/doc/api/fs.md b/doc/api/fs.md index cd47b687beb8a6..154573d7e4ddef 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -545,6 +545,9 @@ the end of the file. -* `data` {string|Buffer|TypedArray|DataView|Object} +* `data` {string|Buffer|TypedArray|DataView|Object|AsyncIterable|Iterable|Stream} * `options` {Object|string} * `encoding` {string|null} The expected character encoding when `data` is a string. **Default:** `'utf8'` * Returns: {Promise} Asynchronously writes data to a file, replacing the file if it already exists. -`data` can be a string, a buffer, or an object with an own `toString` function +`data` can be a string, a buffer, an {AsyncIterable} or {Iterable} object, or an +object with an own `toString` function property. The promise is resolved with no arguments upon success. If `options` is a string, then it specifies the `encoding`. diff --git a/test/parallel/test-fs-promises-file-handle-writeFile.js b/test/parallel/test-fs-promises-file-handle-writeFile.js index 44f049f55a9944..aa9ccb859bcb12 100644 --- a/test/parallel/test-fs-promises-file-handle-writeFile.js +++ b/test/parallel/test-fs-promises-file-handle-writeFile.js @@ -8,6 +8,7 @@ const common = require('../common'); const fs = require('fs'); const { open, writeFile } = fs.promises; const path = require('path'); +const { Readable } = require('stream'); const tmpdir = require('../common/tmpdir'); const assert = require('assert'); const tmpDir = tmpdir.path; @@ -43,6 +44,126 @@ async function doWriteAndCancel() { } } -validateWriteFile() - .then(doWriteAndCancel) - .then(common.mustCall()); +const dest = path.resolve(tmpDir, 'tmp.txt'); +const otherDest = path.resolve(tmpDir, 'tmp-2.txt'); +const stream = Readable.from(['a', 'b', 'c']); +const stream2 = Readable.from(['ümlaut', ' ', 'sechzig']); +const iterable = { + expected: 'abc', + *[Symbol.iterator]() { + yield 'a'; + yield 'b'; + yield 'c'; + } +}; +function iterableWith(value) { + return { + *[Symbol.iterator]() { + yield value; + } + }; +} +const bufferIterable = { + expected: 'abc', + *[Symbol.iterator]() { + yield Buffer.from('a'); + yield Buffer.from('b'); + yield Buffer.from('c'); + } +}; +const asyncIterable = { + expected: 'abc', + async* [Symbol.asyncIterator]() { + yield 'a'; + yield 'b'; + yield 'c'; + } +}; + +async function doWriteStream() { + const fileHandle = await open(dest, 'w+'); + await fileHandle.writeFile(stream); + const expected = 'abc'; + const data = fs.readFileSync(dest, 'utf-8'); + assert.deepStrictEqual(data, expected); +} + +async function doWriteStreamWithCancel() { + const controller = new AbortController(); + const { signal } = controller; + process.nextTick(() => controller.abort()); + const fileHandle = await open(otherDest, 'w+'); + assert.rejects(fileHandle.writeFile(stream, { signal }), { + name: 'AbortError' + }); +} + +async function doWriteIterable() { + const fileHandle = await open(dest, 'w+'); + await fileHandle.writeFile(iterable); + const data = fs.readFileSync(dest, 'utf-8'); + assert.deepStrictEqual(data, iterable.expected); + await fileHandle.close(); +} + +async function doWriteInvalidIterable() { + const fileHandle = await open(dest, 'w+'); + await Promise.all( + [42, 42n, {}, Symbol('42'), true, undefined, null, NaN].map((value) => + assert.rejects(fileHandle.writeFile(iterableWith(value)), { + code: 'ERR_INVALID_ARG_TYPE', + }) + ) + ); + await fileHandle.close(); +} + +async function doWriteIterableWithEncoding() { + const fileHandle = await open(dest, 'w+'); + await fileHandle.writeFile(stream2, 'latin1'); + const expected = 'ümlaut sechzig'; + const data = fs.readFileSync(dest, 'latin1'); + assert.deepStrictEqual(data, expected); + await fileHandle.close(); +} + +async function doWriteBufferIterable() { + const fileHandle = await open(dest, 'w+'); + await fileHandle.writeFile(bufferIterable); + const data = fs.readFileSync(dest, 'utf-8'); + assert.deepStrictEqual(data, bufferIterable.expected); + await fileHandle.close(); +} + +async function doWriteAsyncIterable() { + const fileHandle = await open(dest, 'w+'); + await fileHandle.writeFile(asyncIterable); + const data = fs.readFileSync(dest, 'utf-8'); + assert.deepStrictEqual(data, asyncIterable.expected); + await fileHandle.close(); +} + +async function doWriteInvalidValues() { + const fileHandle = await open(dest, 'w+'); + await Promise.all( + [42, 42n, {}, Symbol('42'), true, undefined, null, NaN].map((value) => + assert.rejects(fileHandle.writeFile(value), { + code: 'ERR_INVALID_ARG_TYPE', + }) + ) + ); + await fileHandle.close(); +} + +(async () => { + await validateWriteFile(); + await doWriteAndCancel(); + await doWriteStream(); + await doWriteStreamWithCancel(); + await doWriteIterable(); + await doWriteInvalidIterable(); + await doWriteIterableWithEncoding(); + await doWriteBufferIterable(); + await doWriteAsyncIterable(); + await doWriteInvalidValues(); +})().then(common.mustCall());