Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
fs: add docs and tests for AsyncIterable support in fh.writeFile
Refs: #37490

PR-URL: #39836
Reviewed-By: Nitzan Uziely <linkgoron@gmail.com>
  • Loading branch information
aduh95 committed Sep 1, 2021
commit 17b6ad6f854e9763dd6b59b67fd376249935c0fd
11 changes: 8 additions & 3 deletions doc/api/fs.md
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,9 @@ the end of the file.
<!-- YAML
added: v10.0.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/37490
description: The `data` argument supports `AsyncIterable`, `Iterable` and `Stream`.
- version: v14.12.0
pr-url: https://github.com/nodejs/node/pull/34993
description: The `data` parameter will stringify an object with an
Expand All @@ -505,14 +508,16 @@ changes:
strings anymore.
-->

* `data` {string|Buffer|Uint8Array|Object}
* `data` {string|Buffer|Uint8Array|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`.
Expand Down Expand Up @@ -1236,7 +1241,7 @@ added: v10.0.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/37490
description: The `data` argument supports `AsyncIterable`, `Iterable` & `Stream`.
description: The `data` argument supports `AsyncIterable`, `Iterable` and `Stream`.
- version: v14.17.0
pr-url: https://github.com/nodejs/node/pull/35993
description: The options argument may include an AbortSignal to abort an
Expand Down
170 changes: 161 additions & 9 deletions test/parallel/test-fs-promises-file-handle-writeFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,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;
Expand All @@ -18,13 +19,15 @@ tmpdir.refresh();
async function validateWriteFile() {
const filePathForHandle = path.resolve(tmpDir, 'tmp-write-file2.txt');
const fileHandle = await open(filePathForHandle, 'w+');
const buffer = Buffer.from('Hello world'.repeat(100), 'utf8');
try {
const buffer = Buffer.from('Hello world'.repeat(100), 'utf8');

await fileHandle.writeFile(buffer);
const readFileData = fs.readFileSync(filePathForHandle);
assert.deepStrictEqual(buffer, readFileData);

await fileHandle.close();
await fileHandle.writeFile(buffer);
const readFileData = fs.readFileSync(filePathForHandle);
assert.deepStrictEqual(buffer, readFileData);
} finally {
await fileHandle.close();
}
}

// Signal aborted while writing file
Expand All @@ -40,6 +43,155 @@ 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+');
try {
await fileHandle.writeFile(stream);
const expected = 'abc';
const data = fs.readFileSync(dest, 'utf-8');
assert.deepStrictEqual(data, expected);
} finally {
await fileHandle.close();
}
}

async function doWriteStreamWithCancel() {
const controller = new AbortController();
const { signal } = controller;
process.nextTick(() => controller.abort());
const fileHandle = await open(otherDest, 'w+');
try {
await assert.rejects(
fileHandle.writeFile(stream, { signal }),
{ name: 'AbortError' }
);
} finally {
await fileHandle.close();
}
}

async function doWriteIterable() {
const fileHandle = await open(dest, 'w+');
try {
await fileHandle.writeFile(iterable);
const data = fs.readFileSync(dest, 'utf-8');
assert.deepStrictEqual(data, iterable.expected);
} finally {
await fileHandle.close();
}
}

async function doWriteInvalidIterable() {
const fileHandle = await open(dest, 'w+');
try {
await Promise.all(
[42, 42n, {}, Symbol('42'), true, undefined, null, NaN].map((value) =>
assert.rejects(
fileHandle.writeFile(iterableWith(value)),
{ code: 'ERR_INVALID_ARG_TYPE' }
)
)
);
} finally {
await fileHandle.close();
}
}

async function doWriteIterableWithEncoding() {
const fileHandle = await open(dest, 'w+');
try {
await fileHandle.writeFile(stream2, 'latin1');
const expected = 'ümlaut sechzig';
const data = fs.readFileSync(dest, 'latin1');
assert.deepStrictEqual(data, expected);
} finally {
await fileHandle.close();
}
}

async function doWriteBufferIterable() {
const fileHandle = await open(dest, 'w+');
try {
await fileHandle.writeFile(bufferIterable);
const data = fs.readFileSync(dest, 'utf-8');
assert.deepStrictEqual(data, bufferIterable.expected);
} finally {
await fileHandle.close();
}
}

async function doWriteAsyncIterable() {
const fileHandle = await open(dest, 'w+');
try {
await fileHandle.writeFile(asyncIterable);
const data = fs.readFileSync(dest, 'utf-8');
assert.deepStrictEqual(data, asyncIterable.expected);
} finally {
await fileHandle.close();
}
}

async function doWriteInvalidValues() {
const fileHandle = await open(dest, 'w+');
try {
await Promise.all(
[42, 42n, {}, Symbol('42'), true, undefined, null, NaN].map((value) =>
assert.rejects(
fileHandle.writeFile(value),
{ code: 'ERR_INVALID_ARG_TYPE' }
)
)
);
} finally {
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());
14 changes: 8 additions & 6 deletions test/parallel/test-fs-promises-writefile.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,10 @@ async function doWriteStreamWithCancel() {
const controller = new AbortController();
const { signal } = controller;
process.nextTick(() => controller.abort());
assert.rejects(fsPromises.writeFile(otherDest, stream, { signal }), {
name: 'AbortError'
});
await assert.rejects(
fsPromises.writeFile(otherDest, stream, { signal }),
{ name: 'AbortError' }
);
}

async function doWriteIterable() {
Expand Down Expand Up @@ -121,9 +122,10 @@ async function doWriteWithCancel() {
const controller = new AbortController();
const { signal } = controller;
process.nextTick(() => controller.abort());
assert.rejects(fsPromises.writeFile(otherDest, buffer, { signal }), {
name: 'AbortError'
});
await assert.rejects(
fsPromises.writeFile(otherDest, buffer, { signal }),
{ name: 'AbortError' }
);
}

async function doAppend() {
Expand Down