| // Flags: --no-warnings --expose-internals |
| 'use strict'; |
| |
| const common = require('../common'); |
| const assert = require('assert'); |
| const { Blob } = require('buffer'); |
| const { inspect } = require('util'); |
| const { EOL } = require('os'); |
| const { kState } = require('internal/webstreams/util'); |
| |
| { |
| const b = new Blob(); |
| assert.strictEqual(b.size, 0); |
| assert.strictEqual(b.type, ''); |
| } |
| |
| assert.throws(() => new Blob(false), { |
| code: 'ERR_INVALID_ARG_TYPE' |
| }); |
| |
| assert.throws(() => new Blob('hello'), { |
| code: 'ERR_INVALID_ARG_TYPE' |
| }); |
| |
| assert.throws(() => new Blob({}), { |
| code: 'ERR_INVALID_ARG_TYPE' |
| }); |
| |
| { |
| const b = new Blob([]); |
| assert(b); |
| assert.strictEqual(b.size, 0); |
| assert.strictEqual(b.type, ''); |
| |
| b.arrayBuffer().then(common.mustCall((ab) => { |
| assert.deepStrictEqual(ab, new ArrayBuffer(0)); |
| })); |
| b.text().then(common.mustCall((text) => { |
| assert.strictEqual(text, ''); |
| })); |
| const c = b.slice(); |
| assert.strictEqual(c.size, 0); |
| } |
| |
| { |
| assert.strictEqual(new Blob([], { type: 1 }).type, '1'); |
| assert.strictEqual(new Blob([], { type: false }).type, 'false'); |
| assert.strictEqual(new Blob([], { type: {} }).type, '[object object]'); |
| } |
| |
| { |
| const b = new Blob([Buffer.from('abc')]); |
| assert.strictEqual(b.size, 3); |
| b.text().then(common.mustCall((text) => { |
| assert.strictEqual(text, 'abc'); |
| })); |
| } |
| |
| { |
| const b = new Blob([new ArrayBuffer(3)]); |
| assert.strictEqual(b.size, 3); |
| b.text().then(common.mustCall((text) => { |
| assert.strictEqual(text, '\0\0\0'); |
| })); |
| } |
| |
| { |
| const b = new Blob([new Uint8Array(3)]); |
| assert.strictEqual(b.size, 3); |
| b.text().then(common.mustCall((text) => { |
| assert.strictEqual(text, '\0\0\0'); |
| })); |
| } |
| |
| { |
| const b = new Blob([new Blob(['abc'])]); |
| assert.strictEqual(b.size, 3); |
| b.text().then(common.mustCall((text) => { |
| assert.strictEqual(text, 'abc'); |
| })); |
| } |
| |
| { |
| const b = new Blob(['hello', Buffer.from('world')]); |
| assert.strictEqual(b.size, 10); |
| b.text().then(common.mustCall((text) => { |
| assert.strictEqual(text, 'helloworld'); |
| })); |
| } |
| |
| { |
| const b = new Blob(['hello', new Uint8Array([0xed, 0xa0, 0x88])]); |
| assert.strictEqual(b.size, 8); |
| b.text().then(common.mustCall((text) => { |
| assert.strictEqual(text, 'hello\ufffd\ufffd\ufffd'); |
| assert.strictEqual(text.length, 8); |
| })); |
| } |
| |
| { |
| const b = new Blob( |
| [ |
| 'h', |
| 'e', |
| 'l', |
| 'lo', |
| Buffer.from('world'), |
| ]); |
| assert.strictEqual(b.size, 10); |
| b.text().then(common.mustCall((text) => { |
| assert.strictEqual(text, 'helloworld'); |
| })); |
| } |
| |
| { |
| const b = new Blob(['hello', Buffer.from('world')]); |
| assert.strictEqual(b.size, 10); |
| assert.strictEqual(b.type, ''); |
| |
| const c = b.slice(1, -1, 'foo'); |
| assert.strictEqual(c.type, 'foo'); |
| c.text().then(common.mustCall((text) => { |
| assert.strictEqual(text, 'elloworl'); |
| })); |
| |
| const d = c.slice(1, -1); |
| d.text().then(common.mustCall((text) => { |
| assert.strictEqual(text, 'llowor'); |
| })); |
| |
| const e = d.slice(1, -1); |
| e.text().then(common.mustCall((text) => { |
| assert.strictEqual(text, 'lowo'); |
| })); |
| |
| const f = e.slice(1, -1); |
| f.text().then(common.mustCall((text) => { |
| assert.strictEqual(text, 'ow'); |
| })); |
| |
| const g = f.slice(1, -1); |
| assert.strictEqual(g.type, ''); |
| g.text().then(common.mustCall((text) => { |
| assert.strictEqual(text, ''); |
| })); |
| |
| const h = b.slice(-1, 1); |
| assert.strictEqual(h.size, 0); |
| |
| const i = b.slice(1, 100); |
| assert.strictEqual(i.size, 9); |
| |
| const j = b.slice(1, 2, false); |
| assert.strictEqual(j.type, 'false'); |
| |
| assert.strictEqual(b.size, 10); |
| assert.strictEqual(b.type, ''); |
| } |
| |
| { |
| const b = new Blob([Buffer.from('hello'), Buffer.from('world')]); |
| const mc = new MessageChannel(); |
| mc.port1.onmessage = common.mustCall(({ data }) => { |
| data.text().then(common.mustCall((text) => { |
| assert.strictEqual(text, 'helloworld'); |
| })); |
| mc.port1.close(); |
| }); |
| mc.port2.postMessage(b); |
| b.text().then(common.mustCall((text) => { |
| assert.strictEqual(text, 'helloworld'); |
| })); |
| } |
| |
| { |
| const b = new Blob(['hello'], { type: '\x01' }); |
| assert.strictEqual(b.type, ''); |
| } |
| |
| { |
| const descriptor = |
| Object.getOwnPropertyDescriptor(Blob.prototype, Symbol.toStringTag); |
| assert.deepStrictEqual(descriptor, { |
| configurable: true, |
| enumerable: false, |
| value: 'Blob', |
| writable: false |
| }); |
| } |
| |
| { |
| const descriptors = Object.getOwnPropertyDescriptors(Blob.prototype); |
| const enumerable = [ |
| 'size', |
| 'type', |
| 'slice', |
| 'stream', |
| 'text', |
| 'arrayBuffer', |
| ]; |
| |
| for (const prop of enumerable) { |
| assert.notStrictEqual(descriptors[prop], undefined); |
| assert.strictEqual(descriptors[prop].enumerable, true); |
| } |
| } |
| |
| { |
| const b = new Blob(['test', 42]); |
| b.text().then(common.mustCall((text) => { |
| assert.strictEqual(text, 'test42'); |
| })); |
| } |
| |
| { |
| const b = new Blob(); |
| assert.strictEqual(inspect(b, { depth: null }), |
| 'Blob { size: 0, type: \'\' }'); |
| assert.strictEqual(inspect(b, { depth: 1 }), |
| 'Blob { size: 0, type: \'\' }'); |
| assert.strictEqual(inspect(b, { depth: -1 }), '[Blob]'); |
| } |
| |
| { |
| // The Blob has to be over a specific size for the data to |
| // be copied asynchronously.. |
| const b = new Blob(['hello', 'there'.repeat(820)]); |
| b.arrayBuffer().then(common.mustCall()); |
| } |
| |
| (async () => { |
| const b = new Blob(['hello']); |
| const reader = b.stream().getReader(); |
| let res = await reader.read(); |
| assert.strictEqual(res.value.byteLength, 5); |
| assert(!res.done); |
| res = await reader.read(); |
| assert(res.done); |
| })().then(common.mustCall()); |
| |
| (async () => { |
| const b = new Blob(Array(10).fill('hello')); |
| const reader = b.stream().getReader(); |
| const chunks = []; |
| while (true) { |
| const res = await reader.read(); |
| if (res.done) break; |
| assert.strictEqual(res.value.byteLength, 5); |
| chunks.push(res.value); |
| } |
| assert.strictEqual(chunks.length, 10); |
| })().then(common.mustCall()); |
| |
| (async () => { |
| const b = new Blob(Array(10).fill('hello')); |
| const reader = b.stream().getReader(); |
| const chunks = []; |
| while (true) { |
| const res = await reader.read(); |
| if (chunks.length === 5) { |
| reader.cancel('boom'); |
| break; |
| } |
| if (res.done) break; |
| assert.strictEqual(res.value.byteLength, 5); |
| chunks.push(res.value); |
| } |
| assert.strictEqual(chunks.length, 5); |
| reader.closed.then(common.mustCall()); |
| })().then(common.mustCall()); |
| |
| (async () => { |
| const b = new Blob(['A', 'B', 'C']); |
| const stream = b.stream(); |
| const chunks = []; |
| const decoder = new TextDecoder(); |
| await stream.pipeTo(new WritableStream({ |
| write(chunk) { |
| chunks.push(decoder.decode(chunk, { stream: true })); |
| } |
| })); |
| assert.strictEqual(chunks.join(''), 'ABC'); |
| })().then(common.mustCall()); |
| |
| (async () => { |
| const b = new Blob(['A', 'B', 'C']); |
| const stream = b.stream(); |
| const chunks = []; |
| const decoder = new TextDecoder(); |
| await stream.pipeTo( |
| new WritableStream({ |
| write(chunk) { |
| chunks.push(decoder.decode(chunk, { stream: true })); |
| }, |
| }) |
| ); |
| assert.strictEqual(chunks.join(''), 'ABC'); |
| })().then(common.mustCall()); |
| |
| (async () => { |
| // Ref: https://github.com/nodejs/node/issues/48668 |
| const chunks = []; |
| const stream = new Blob(['Hello world']).stream(); |
| const decoder = new TextDecoder(); |
| await Promise.resolve(); |
| await stream.pipeTo( |
| new WritableStream({ |
| write(chunk) { |
| chunks.push(decoder.decode(chunk, { stream: true })); |
| }, |
| }) |
| ); |
| assert.strictEqual(chunks.join(''), 'Hello world'); |
| })().then(common.mustCall()); |
| |
| (async () => { |
| // Ref: https://github.com/nodejs/node/issues/48668 |
| if (common.hasCrypto) { |
| // Can only do this test if we have node built with crypto |
| const file = new Blob(['<svg></svg>'], { type: 'image/svg+xml' }); |
| const url = URL.createObjectURL(file); |
| const res = await fetch(url); |
| const blob = await res.blob(); |
| assert.strictEqual(blob.size, 11); |
| assert.strictEqual(blob.type, 'image/svg+xml'); |
| assert.strictEqual(await blob.text(), '<svg></svg>'); |
| } |
| })().then(common.mustCall()); |
| |
| (async () => { |
| const b = new Blob(Array(10).fill('hello')); |
| const stream = b.stream(); |
| const reader = stream.getReader(); |
| assert.strictEqual(stream[kState].controller.desiredSize, 0); |
| const { value, done } = await reader.read(); |
| assert.strictEqual(value.byteLength, 5); |
| assert(!done); |
| setTimeout(() => { |
| // The blob stream is now a byte stream hence after the first read, |
| // it should pull in the next 'hello' which is 5 bytes hence -5. |
| assert.strictEqual(stream[kState].controller.desiredSize, -5); |
| }, 0); |
| })().then(common.mustCall()); |
| |
| (async () => { |
| const blob = new Blob(['hello', 'world']); |
| const stream = blob.stream(); |
| const reader = stream.getReader({ mode: 'byob' }); |
| const decoder = new TextDecoder(); |
| const chunks = []; |
| while (true) { |
| const { value, done } = await reader.read(new Uint8Array(100)); |
| if (done) break; |
| chunks.push(decoder.decode(value, { stream: true })); |
| } |
| assert.strictEqual(chunks.join(''), 'helloworld'); |
| })().then(common.mustCall()); |
| |
| (async () => { |
| const b = new Blob(Array(10).fill('hello')); |
| const stream = b.stream(); |
| const reader = stream.getReader({ mode: 'byob' }); |
| assert.strictEqual(stream[kState].controller.desiredSize, 0); |
| const { value, done } = await reader.read(new Uint8Array(100)); |
| assert.strictEqual(value.byteLength, 5); |
| assert(!done); |
| setTimeout(() => { |
| assert.strictEqual(stream[kState].controller.desiredSize, -5); |
| }, 0); |
| })().then(common.mustCall()); |
| |
| (async () => { |
| const b = new Blob(Array(10).fill('hello')); |
| const stream = b.stream(); |
| const reader = stream.getReader({ mode: 'byob' }); |
| assert.strictEqual(stream[kState].controller.desiredSize, 0); |
| const { value, done } = await reader.read(new Uint8Array(2)); |
| assert.strictEqual(value.byteLength, 2); |
| assert(!done); |
| setTimeout(() => { |
| assert.strictEqual(stream[kState].controller.desiredSize, -3); |
| }, 0); |
| })().then(common.mustCall()); |
| |
| { |
| const b = new Blob(['hello\n'], { endings: 'native' }); |
| assert.strictEqual(b.size, EOL.length + 5); |
| |
| [1, {}, 'foo'].forEach((endings) => { |
| assert.throws(() => new Blob([], { endings }), { |
| code: 'ERR_INVALID_ARG_VALUE', |
| }); |
| }); |
| } |
| |
| { |
| assert.throws(() => Reflect.get(Blob.prototype, 'type', {}), { |
| code: 'ERR_INVALID_THIS', |
| }); |
| assert.throws(() => Reflect.get(Blob.prototype, 'size', {}), { |
| code: 'ERR_INVALID_THIS', |
| }); |
| assert.throws(() => Blob.prototype.slice(Blob.prototype, 0, 1), { |
| code: 'ERR_INVALID_THIS', |
| }); |
| assert.throws(() => Blob.prototype.stream.call(), { |
| code: 'ERR_INVALID_THIS', |
| }); |
| } |
| |
| (async () => { |
| await assert.rejects(async () => Blob.prototype.arrayBuffer.call(), { |
| code: 'ERR_INVALID_THIS', |
| }); |
| await assert.rejects(async () => Blob.prototype.text.call(), { |
| code: 'ERR_INVALID_THIS', |
| }); |
| })().then(common.mustCall()); |
| |
| (async () => { |
| const blob = new Blob([ |
| new Uint8Array([0x50, 0x41, 0x53, 0x53]), |
| new Int8Array([0x50, 0x41, 0x53, 0x53]), |
| new Uint16Array([0x4150, 0x5353]), |
| new Int16Array([0x4150, 0x5353]), |
| new Uint32Array([0x53534150]), |
| new Int32Array([0x53534150]), |
| new Float32Array([0xD341500000]), |
| ]); |
| |
| assert.strictEqual(blob.size, 28); |
| assert.strictEqual(blob.type, ''); |
| })().then(common.mustCall()); |
| |
| { |
| // Testing the defaults |
| [undefined, null, { __proto__: null }, { type: undefined }, { |
| get type() {}, // eslint-disable-line getter-return |
| }].forEach((options) => { |
| assert.strictEqual( |
| new Blob([], options).type, |
| new Blob([]).type, |
| ); |
| }); |
| |
| Reflect.defineProperty(Object.prototype, 'type', { |
| __proto__: null, |
| configurable: true, |
| get: common.mustCall(() => 3, 7), |
| }); |
| |
| [{}, [], () => {}, Number, new Number(), new String(), new Boolean()].forEach( |
| (options) => { |
| assert.strictEqual(new Blob([], options).type, '3'); |
| }, |
| ); |
| [0, '', true, Symbol(), 0n].forEach((options) => { |
| assert.throws(() => new Blob([], options), { code: 'ERR_INVALID_ARG_TYPE' }); |
| }); |
| |
| delete Object.prototype.type; |
| } |
| |
| (async () => { |
| // Refs: https://github.com/nodejs/node/issues/47301 |
| |
| const random = Buffer.alloc(256).fill('0'); |
| const chunks = []; |
| |
| for (let i = 0; i < random.length; i += 2) { |
| chunks.push(random.subarray(i, i + 2)); |
| } |
| |
| await new Blob(chunks).arrayBuffer(); |
| })().then(common.mustCall()); |
| |
| { |
| const blob = new Blob(['hello']); |
| |
| assert.ok(blob.slice(0, 1).constructor === Blob); |
| assert.ok(blob.slice(0, 1) instanceof Blob); |
| } |
| |
| (async () => { |
| const blob = new Blob(['hello']); |
| |
| assert.ok(structuredClone(blob).constructor === Blob); |
| assert.ok(structuredClone(blob) instanceof Blob); |
| assert.ok(structuredClone(blob).size === blob.size); |
| assert.ok(structuredClone(blob).size === blob.size); |
| assert.ok((await structuredClone(blob).text()) === (await blob.text())); |
| })().then(common.mustCall()); |