buffer: introduce File

PR-URL: https://github.com/nodejs/node/pull/45139
Fixes: https://github.com/nodejs/node/issues/39015
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
This commit is contained in:
Khafra 2022-11-10 15:52:45 -05:00 committed by GitHub
parent e9a9e1ea5b
commit 916af4ef2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 804 additions and 0 deletions

34
benchmark/blob/file.js Normal file
View File

@ -0,0 +1,34 @@
'use strict';
const common = require('../common.js');
const { File } = require('buffer');
const bench = common.createBenchmark(main, {
bytes: [128, 1024, 1024 ** 2],
n: [1e6],
operation: ['text', 'arrayBuffer']
});
const options = {
lastModified: Date.now() - 1e6,
};
async function run(n, bytes, operation) {
const buff = Buffer.allocUnsafe(bytes);
const source = new File(buff, 'dummy.txt', options);
bench.start();
for (let i = 0; i < n; i++) {
switch (operation) {
case 'text':
await source.text();
break;
case 'arrayBuffer':
await source.arrayBuffer();
break;
}
}
bench.end(n);
}
function main(conf) {
run(conf.n, conf.bytes, conf.operation).catch(console.log);
}

View File

@ -5013,6 +5013,56 @@ changes:
See [`Buffer.from(string[, encoding])`][`Buffer.from(string)`].
## Class: `File`
<!-- YAML
added: REPLACEME
-->
> Stability: 1 - Experimental
* Extends: {Blob}
A [`File`][] provides information about files.
### `new buffer.File(sources, fileName[, options])`
<!-- YAML
added: REPLACEME
-->
* `sources` {string\[]|ArrayBuffer\[]|TypedArray\[]|DataView\[]|Blob\[]|File\[]}
An array of string, {ArrayBuffer}, {TypedArray}, {DataView}, {File}, or {Blob}
objects, or any mix of such objects, that will be stored within the `File`.
* `fileName` {string} The name of the file.
* `options` {Object}
* `endings` {string} One of either `'transparent'` or `'native'`. When set
to `'native'`, line endings in string source parts will be converted to
the platform native line-ending as specified by `require('node:os').EOL`.
* `type` {string} The File content-type.
* `lastModified` {number} The last modified date of the file.
**Default:** `Date.now()`.
### `file.name`
<!-- YAML
added: REPLACEME
-->
* Type: {string}
The name of the `File`.
### `file.lastModified`
<!-- YAML
added: REPLACEME
-->
* Type: {number}
The last modified date of the `File`.
## `node:buffer` module APIs
While, the `Buffer` object is available as a global, there are additional
@ -5359,6 +5409,7 @@ introducing security vulnerabilities into an application.
[`ERR_INVALID_ARG_VALUE`]: errors.md#err_invalid_arg_value
[`ERR_INVALID_BUFFER_SIZE`]: errors.md#err_invalid_buffer_size
[`ERR_OUT_OF_RANGE`]: errors.md#err_out_of_range
[`File`]: https://developer.mozilla.org/en-US/docs/Web/API/File
[`JSON.stringify()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
[`SharedArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
[`String.prototype.indexOf()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf

View File

@ -126,6 +126,10 @@ const {
resolveObjectURL,
} = require('internal/blob');
const {
File,
} = require('internal/file');
FastBuffer.prototype.constructor = Buffer;
Buffer.prototype = FastBuffer.prototype;
addBufferPrototypeMethods(Buffer.prototype);
@ -1320,6 +1324,7 @@ function atob(input) {
module.exports = {
Blob,
File,
resolveObjectURL,
Buffer,
SlowBuffer,

113
lib/internal/file.js Normal file
View File

@ -0,0 +1,113 @@
'use strict';
const {
DateNow,
NumberIsNaN,
ObjectDefineProperties,
SymbolToStringTag,
} = primordials;
const {
Blob,
} = require('internal/blob');
const {
customInspectSymbol: kInspect,
emitExperimentalWarning,
kEnumerableProperty,
kEmptyObject,
toUSVString,
} = require('internal/util');
const {
codes: {
ERR_INVALID_THIS,
ERR_MISSING_ARGS,
},
} = require('internal/errors');
const {
inspect,
} = require('internal/util/inspect');
class File extends Blob {
/** @type {string} */
#name;
/** @type {number} */
#lastModified;
constructor(fileBits, fileName, options = kEmptyObject) {
emitExperimentalWarning('buffer.File');
if (arguments.length < 2) {
throw new ERR_MISSING_ARGS('fileBits', 'fileName');
}
super(fileBits, options);
let { lastModified } = options ?? kEmptyObject;
if (lastModified !== undefined) {
// Using Number(...) will not throw an error for bigints.
lastModified = +lastModified;
if (NumberIsNaN(lastModified)) {
lastModified = 0;
}
} else {
lastModified = DateNow();
}
this.#name = toUSVString(fileName);
this.#lastModified = lastModified;
}
get name() {
if (!this || !(#name in this)) {
throw new ERR_INVALID_THIS('File');
}
return this.#name;
}
get lastModified() {
if (!this || !(#name in this)) {
throw new ERR_INVALID_THIS('File');
}
return this.#lastModified;
}
[kInspect](depth, options) {
if (depth < 0) {
return this;
}
const opts = {
...options,
depth: options.depth == null ? null : options.depth - 1,
};
return `File ${inspect({
size: this.size,
type: this.type,
name: this.#name,
lastModified: this.#lastModified,
}, opts)}`;
}
}
ObjectDefineProperties(File.prototype, {
name: kEnumerableProperty,
lastModified: kEnumerableProperty,
[SymbolToStringTag]: {
__proto__: null,
configurable: true,
value: 'File',
}
});
module.exports = {
File,
};

View File

@ -0,0 +1,155 @@
// META: title=File constructor
const to_string_obj = { toString: () => 'a string' };
const to_string_throws = { toString: () => { throw new Error('expected'); } };
test(function() {
assert_true("File" in globalThis, "globalThis should have a File property.");
}, "File interface object exists");
test(t => {
assert_throws_js(TypeError, () => new File(),
'Bits argument is required');
assert_throws_js(TypeError, () => new File([]),
'Name argument is required');
}, 'Required arguments');
function test_first_argument(arg1, expectedSize, testName) {
test(function() {
var file = new File(arg1, "dummy");
assert_true(file instanceof File);
assert_equals(file.name, "dummy");
assert_equals(file.size, expectedSize);
assert_equals(file.type, "");
// assert_false(file.isClosed); XXX: File.isClosed doesn't seem to be implemented
assert_not_equals(file.lastModified, "");
}, testName);
}
test_first_argument([], 0, "empty fileBits");
test_first_argument(["bits"], 4, "DOMString fileBits");
test_first_argument(["𝓽𝓮𝔁𝓽"], 16, "Unicode DOMString fileBits");
test_first_argument([new String('string object')], 13, "String object fileBits");
test_first_argument([new Blob()], 0, "Empty Blob fileBits");
test_first_argument([new Blob(["bits"])], 4, "Blob fileBits");
test_first_argument([new File([], 'world.txt')], 0, "Empty File fileBits");
test_first_argument([new File(["bits"], 'world.txt')], 4, "File fileBits");
test_first_argument([new ArrayBuffer(8)], 8, "ArrayBuffer fileBits");
test_first_argument([new Uint8Array([0x50, 0x41, 0x53, 0x53])], 4, "Typed array fileBits");
test_first_argument(["bits", new Blob(["bits"]), new Blob(), new Uint8Array([0x50, 0x41]),
new Uint16Array([0x5353]), new Uint32Array([0x53534150])], 16, "Various fileBits");
test_first_argument([12], 2, "Number in fileBits");
test_first_argument([[1,2,3]], 5, "Array in fileBits");
test_first_argument([{}], 15, "Object in fileBits"); // "[object Object]"
if (globalThis.document !== undefined) {
test_first_argument([document.body], 24, "HTMLBodyElement in fileBits"); // "[object HTMLBodyElement]"
}
test_first_argument([to_string_obj], 8, "Object with toString in fileBits");
test_first_argument({[Symbol.iterator]() {
let i = 0;
return {next: () => [
{done:false, value:'ab'},
{done:false, value:'cde'},
{done:true}
][i++]};
}}, 5, 'Custom @@iterator');
[
'hello',
0,
null
].forEach(arg => {
test(t => {
assert_throws_js(TypeError, () => new File(arg, 'world.html'),
'Constructor should throw for invalid bits argument');
}, `Invalid bits argument: ${JSON.stringify(arg)}`);
});
test(t => {
assert_throws_js(Error, () => new File([to_string_throws], 'name.txt'),
'Constructor should propagate exceptions');
}, 'Bits argument: object that throws');
function test_second_argument(arg2, expectedFileName, testName) {
test(function() {
var file = new File(["bits"], arg2);
assert_true(file instanceof File);
assert_equals(file.name, expectedFileName);
}, testName);
}
test_second_argument("dummy", "dummy", "Using fileName");
test_second_argument("dummy/foo", "dummy/foo",
"No replacement when using special character in fileName");
test_second_argument(null, "null", "Using null fileName");
test_second_argument(1, "1", "Using number fileName");
test_second_argument('', '', "Using empty string fileName");
if (globalThis.document !== undefined) {
test_second_argument(document.body, '[object HTMLBodyElement]', "Using object fileName");
}
// testing the third argument
[
{type: 'text/plain', expected: 'text/plain'},
{type: 'text/plain;charset=UTF-8', expected: 'text/plain;charset=utf-8'},
{type: 'TEXT/PLAIN', expected: 'text/plain'},
{type: '𝓽𝓮𝔁𝓽/𝔭𝔩𝔞𝔦𝔫', expected: ''},
{type: 'ascii/nonprintable\u001F', expected: ''},
{type: 'ascii/nonprintable\u007F', expected: ''},
{type: 'nonascii\u00EE', expected: ''},
{type: 'nonascii\u1234', expected: ''},
{type: 'nonparsable', expected: 'nonparsable'}
].forEach(testCase => {
test(t => {
var file = new File(["bits"], "dummy", { type: testCase.type});
assert_true(file instanceof File);
assert_equals(file.type, testCase.expected);
}, `Using type in File constructor: ${testCase.type}`);
});
test(function() {
var file = new File(["bits"], "dummy", { lastModified: 42 });
assert_true(file instanceof File);
assert_equals(file.lastModified, 42);
}, "Using lastModified");
test(function() {
var file = new File(["bits"], "dummy", { name: "foo" });
assert_true(file instanceof File);
assert_equals(file.name, "dummy");
}, "Misusing name");
test(function() {
var file = new File(["bits"], "dummy", { unknownKey: "value" });
assert_true(file instanceof File);
assert_equals(file.name, "dummy");
}, "Unknown properties are ignored");
[
123,
123.4,
true,
'abc'
].forEach(arg => {
test(t => {
assert_throws_js(TypeError, () => new File(['bits'], 'name.txt', arg),
'Constructor should throw for invalid property bag type');
}, `Invalid property bag: ${JSON.stringify(arg)}`);
});
[
null,
undefined,
[1,2,3],
/regex/,
function() {}
].forEach(arg => {
test(t => {
assert_equals(new File(['bits'], 'name.txt', arg).size, 4,
'Constructor should accept object-ish property bag type');
}, `Unusual but valid property bag: ${arg}`);
});
test(t => {
assert_throws_js(Error,
() => new File(['bits'], 'name.txt', {type: to_string_throws}),
'Constructor should propagate exceptions');
}, 'Property bag propagates exceptions');

View File

@ -0,0 +1,69 @@
// META: title=FormData: FormData: Upload files named using controls
// META: script=../support/send-file-formdata-helper.js
"use strict";
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-NUL-[\0].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-BS-[\b].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-VT-[\v].txt",
});
// These have characters that undergo processing in name=,
// filename=, and/or value; formDataPostFileUploadTest postprocesses
// expectedEncodedBaseName for these internally.
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LF-[\n].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LF-CR-[\n\r].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-CR-[\r].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-CR-LF-[\r\n].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-HT-[\t].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-FF-[\f].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-DEL-[\x7F].txt",
});
// The rest should be passed through unmodified:
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-ESC-[\x1B].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-SPACE-[ ].txt",
});

View File

@ -0,0 +1,144 @@
// META: title=FormData: FormData: Upload files named using punctuation
// META: script=../support/send-file-formdata-helper.js
"use strict";
// These have characters that undergo processing in name=,
// filename=, and/or value; formDataPostFileUploadTest postprocesses
// expectedEncodedBaseName for these internally.
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-QUOTATION-MARK-[\x22].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: '"file-for-upload-in-form-double-quoted.txt"',
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-REVERSE-SOLIDUS-[\\].txt",
});
// The rest should be passed through unmodified:
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-EXCLAMATION-MARK-[!].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-DOLLAR-SIGN-[$].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-PERCENT-SIGN-[%].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-AMPERSAND-[&].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-APOSTROPHE-['].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LEFT-PARENTHESIS-[(].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-RIGHT-PARENTHESIS-[)].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-ASTERISK-[*].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-PLUS-SIGN-[+].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-COMMA-[,].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-FULL-STOP-[.].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-SOLIDUS-[/].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-COLON-[:].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-SEMICOLON-[;].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-EQUALS-SIGN-[=].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-QUESTION-MARK-[?].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-CIRCUMFLEX-ACCENT-[^].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LEFT-SQUARE-BRACKET-[[].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-RIGHT-SQUARE-BRACKET-[]].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-LEFT-CURLY-BRACKET-[{].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-VERTICAL-LINE-[|].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-RIGHT-CURLY-BRACKET-[}].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form-TILDE-[~].txt",
});
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "'file-for-upload-in-form-single-quoted.txt'",
});

View File

@ -0,0 +1,33 @@
// META: title=FormData: FormData: Upload files in UTF-8 fetch()
// META: script=../support/send-file-formdata-helper.js
"use strict";
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form.txt",
});
formDataPostFileUploadTest({
fileNameSource: "x-user-defined",
fileBaseName: "file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt",
});
formDataPostFileUploadTest({
fileNameSource: "windows-1252",
fileBaseName: "file-for-upload-in-form-☺😂.txt",
});
formDataPostFileUploadTest({
fileNameSource: "JIS X 0201 and JIS X 0208",
fileBaseName: "file-for-upload-in-form-★星★.txt",
});
formDataPostFileUploadTest({
fileNameSource: "Unicode",
fileBaseName: "file-for-upload-in-form-☺😂.txt",
});
formDataPostFileUploadTest({
fileNameSource: "Unicode",
fileBaseName: `file-for-upload-in-form-${kTestChars}.txt`,
});

View File

@ -0,0 +1,8 @@
// META: title=FormData: Upload ASCII-named file in UTF-8 form
// META: script=../support/send-file-formdata-helper.js
"use strict";
formDataPostFileUploadTest({
fileNameSource: "ASCII",
fileBaseName: "file-for-upload-in-form.txt",
});

View File

@ -17,6 +17,7 @@ Last update:
- encoding: https://github.com/web-platform-tests/wpt/tree/c1b24fce6e/encoding
- fetch/data-urls/resources: https://github.com/web-platform-tests/wpt/tree/7c79d998ff/fetch/data-urls/resources
- FileAPI: https://github.com/web-platform-tests/wpt/tree/3b279420d4/FileAPI
- FileAPI/file: https://github.com/web-platform-tests/wpt/tree/c01f637cca/FileAPI/file
- hr-time: https://github.com/web-platform-tests/wpt/tree/34cafd797e/hr-time
- html/webappapis/atob: https://github.com/web-platform-tests/wpt/tree/f267e1dca6/html/webappapis/atob
- html/webappapis/microtask-queuing: https://github.com/web-platform-tests/wpt/tree/2c5c3c4c27/html/webappapis/microtask-queuing

View File

@ -27,6 +27,10 @@
"commit": "3b279420d40afea32506e823f9ac005448f4f3d8",
"path": "FileAPI"
},
"FileAPI/file": {
"commit": "c01f637cca43f0e08ce8e4269121dcd89ccbdd82",
"path": "FileAPI/file"
},
"hr-time": {
"commit": "34cafd797e58dad280d20040eee012d49ccfa91f",
"path": "hr-time"

View File

@ -165,6 +165,7 @@ const expectedModules = new Set([
'NativeModule internal/worker/js_transferable',
'Internal Binding blob',
'NativeModule internal/blob',
'NativeModule internal/file',
'NativeModule async_hooks',
'NativeModule net',
'NativeModule path',

155
test/parallel/test-file.js Normal file
View File

@ -0,0 +1,155 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { Blob, File } = require('buffer');
const { inspect } = require('util');
{
// ensure File extends Blob
assert.deepStrictEqual(Object.getPrototypeOf(File.prototype), Blob.prototype);
}
{
assert.throws(() => new File(), TypeError);
assert.throws(() => new File([]), TypeError);
}
{
const properties = ['name', 'lastModified'];
for (const prop of properties) {
const desc = Object.getOwnPropertyDescriptor(File.prototype, prop);
assert.notStrictEqual(desc, undefined);
// Ensure these properties are getters.
assert.strictEqual(desc.get?.name, `get ${prop}`);
assert.strictEqual(desc.set, undefined);
assert.strictEqual(desc.enumerable, true);
assert.strictEqual(desc.configurable, true);
}
}
{
const file = new File([], '');
assert.strictEqual(file[Symbol.toStringTag], 'File');
assert.strictEqual(File.prototype[Symbol.toStringTag], 'File');
}
{
assert.throws(() => File.prototype.name, TypeError);
assert.throws(() => File.prototype.lastModified, TypeError);
}
{
const keys = Object.keys(File.prototype).sort();
assert.deepStrictEqual(keys, ['lastModified', 'name']);
}
{
const file = new File([], 'dummy.txt.exe');
assert.strictEqual(file.name, 'dummy.txt.exe');
assert.strictEqual(file.size, 0);
assert.strictEqual(typeof file.lastModified, 'number');
assert(file.lastModified <= Date.now());
}
{
const emptyFile = new File([], 'empty.txt');
const blob = new Blob(['hello world']);
emptyFile.text.call(blob).then(common.mustCall((text) => {
assert.strictEqual(text, 'hello world');
}));
}
{
const toPrimitive = {
[Symbol.toPrimitive]() {
return 'NaN';
}
};
const invalidLastModified = [
null,
'string',
false,
toPrimitive,
];
for (const lastModified of invalidLastModified) {
const file = new File([], '', { lastModified });
assert.strictEqual(file.lastModified, 0);
}
}
{
const file = new File([], '', { lastModified: undefined });
assert.notStrictEqual(file.lastModified, 0);
}
{
const toPrimitive = {
[Symbol.toPrimitive]() {
throw new TypeError('boom');
}
};
const throwValues = [
BigInt(3n),
toPrimitive,
];
for (const lastModified of throwValues) {
assert.throws(() => new File([], '', { lastModified }), TypeError);
}
}
{
const valid = [
{
[Symbol.toPrimitive]() {
return 10;
}
},
new Number(10),
10,
];
for (const lastModified of valid) {
assert.strictEqual(new File([], '', { lastModified }).lastModified, 10);
}
}
{
const file = new File([], '');
assert(inspect(file).startsWith('File { size: 0, type: \'\', name: \'\', lastModified:'));
}
{
function MyClass() {}
MyClass.prototype.lastModified = 10;
const file = new File([], '', new MyClass());
assert.strictEqual(file.lastModified, 10);
}
{
let counter = 0;
new File([], '', {
get lastModified() {
counter++;
return 10;
}
});
assert.strictEqual(counter, 1);
}
{
const getter = Object.getOwnPropertyDescriptor(File.prototype, 'name').get;
assert.throws(
() => getter.call(undefined), // eslint-disable-line no-useless-call
{
code: 'ERR_INVALID_THIS',
}
);
}

View File

@ -0,0 +1,17 @@
{
"Worker-read-file-constructor.worker.js": {
"skip": true
},
"send-file-formdata-punctuation.any.js": {
"skip": true
},
"send-file-formdata-utf-8.any.js": {
"skip": true
},
"send-file-formdata.any.js": {
"skip": true
},
"send-file-formdata-controls.any.js": {
"skip": true
}
}

13
test/wpt/test-file.js Normal file
View File

@ -0,0 +1,13 @@
'use strict';
require('../common');
const { WPTRunner } = require('../common/wpt');
const runner = new WPTRunner('FileAPI/file');
runner.setInitScript(`
const { File } = require('buffer');
globalThis.File = File;
`);
runner.runJsTests();

View File

@ -43,6 +43,7 @@ const customTypesMap = {
`${jsDocPrefix}Reference/Global_Objects/WebAssembly/Instance`,
'Blob': 'buffer.html#class-blob',
'File': 'buffer.html#class-file',
'BroadcastChannel':
'worker_threads.html#class-broadcastchannel-' +