Skip to content

found/fixed some errors while testing agains WPT #109

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 2 commits into from
Jul 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 3 additions & 1 deletion file.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ const _File = class File extends Blob {
}
super(fileBits, options);

if (options === null) options = {};

const modified = Number(options.lastModified);
this.#lastModified = Number.isNaN(modified) ? Date.now() : modified
this.#name = fileName;
this.#name = String(fileName);
}

get name() {
Expand Down
12 changes: 11 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ const _Blob = class Blob {
constructor(blobParts = [], options = {}) {
const parts = [];
let size = 0;
if (typeof blobParts !== 'object') {
throw new TypeError(`Failed to construct 'Blob': parameter 1 is not an iterable object.`);
}

if (typeof options !== 'object' && typeof options !== 'function') {
throw new TypeError(`Failed to construct 'Blob': parameter 2 cannot convert to dictionary.`);
}

if (options === null) options = {};

for (const element of blobParts) {
let part;
Expand All @@ -79,7 +88,7 @@ const _Blob = class Blob {

const type = options.type === undefined ? '' : String(options.type);

this.#type = /[^\u0020-\u007E]/.test(type) ? '' : type;
this.#type = /^[\x20-\x7E]*$/.test(type) ? type : '';
this.#size = size;
this.#parts = parts;
}
Expand Down Expand Up @@ -195,6 +204,7 @@ const _Blob = class Blob {
chunk = part.slice(relativeStart, Math.min(size, relativeEnd));
added += chunk.size
}
relativeEnd -= size;
blobParts.push(chunk);
relativeStart = 0; // All next sequential parts should start at 0
}
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
],
"scripts": {
"lint": "xo test.js",
"test": "npm run lint && ava",
"report": "c8 --reporter json --reporter text ava",
"coverage": "c8 --reporter json --reporter text ava && codecov -f coverage/coverage-final.json",
"test-wpt": "node --experimental-loader ./test/http-loader.js ./test/test-wpt-in-node.js",
"test": "npm run lint && ava test.js",
"report": "c8 --reporter json --reporter text ava test.js",
"coverage": "c8 --reporter json --reporter text ava test.js && codecov -f coverage/coverage-final.json",
"prepublishOnly": "tsc --declaration --emitDeclarationOnly --allowJs index.js from.js"
},
"repository": "https://github.com/node-fetch/fetch-blob.git",
Expand Down
7 changes: 2 additions & 5 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,8 @@ test('Blob ctor reads blob parts from object with @@iterator', async t => {
t.is(await blob.text(), expected);
});

test('Blob ctor threats a string as a sequence', async t => {
const expected = 'abc';
const blob = new Blob(expected);

t.is(await blob.text(), expected);
test('Blob ctor throws a string', t => {
t.throws(() => new Blob('abc'));
});

test('Blob ctor threats Uint8Array as a sequence', async t => {
Expand Down
51 changes: 51 additions & 0 deletions test/http-loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// https-loader.mjs
import { get } from 'https';

export function resolve(specifier, context, defaultResolve) {
const { parentURL = null } = context;

// Normally Node.js would error on specifiers starting with 'https://', so
// this hook intercepts them and converts them into absolute URLs to be
// passed along to the later hooks below.
if (specifier.startsWith('https://')) {
return {
url: specifier
};
} else if (parentURL && parentURL.startsWith('https://')) {
return {
url: new URL(specifier, parentURL).href
};
}

// Let Node.js handle all other specifiers.
return defaultResolve(specifier, context, defaultResolve);
}

export function getFormat(url, context, defaultGetFormat) {
// This loader assumes all network-provided JavaScript is ES module code.
if (url.startsWith('https://')) {
return {
format: 'module'
};
}

// Let Node.js handle all other URLs.
return defaultGetFormat(url, context, defaultGetFormat);
}

export function getSource(url, context, defaultGetSource) {
// For JavaScript to be loaded over the network, we need to fetch and
// return it.
if (url.startsWith('https://')) {
return new Promise((resolve, reject) => {
let data = ''
get(url, async res => {
for await (const chunk of res) data += chunk;
resolve({ source: data });
}).on('error', (err) => reject(err));
});
}

// Let Node.js handle all other URLs.
return defaultGetSource(url, context, defaultGetSource);
}
133 changes: 133 additions & 0 deletions test/test-wpt-in-node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Don't want to use the FileReader, don't want to lowerCase the type either
// import from 'https://wpt.live/resources/testharnessreport.js'
import {File, Blob} from '../from.js'

globalThis.self = globalThis
await import('https://wpt.live/resources/testharness.js')

// Should probably be fixed... should be able to compare a Blob to a File
delete Blob[Symbol.hasInstance]

setup({
explicit_timeout: true,
explicit_done: true,
});

function test_blob(fn, expectations) {
var expected = expectations.expected,
type = expectations.type,
desc = expectations.desc;

var t = async_test(desc);
t.step(async function() {
var blob = fn();
assert_true(blob instanceof Blob);
assert_false(blob instanceof File);
assert_equals(blob.type.toLowerCase(), type);
assert_equals(blob.size, expected.length);
assert_equals(await blob.text(), expected);
t.done();
});
}

function test_blob_binary(fn, expectations) {
var expected = expectations.expected,
type = expectations.type,
desc = expectations.desc;

var t = async_test(desc);
t.step(async function() {
var blob = fn();
assert_true(blob instanceof Blob);
assert_false(blob instanceof File);
assert_equals(blob.type.toLowerCase(), type);
assert_equals(blob.size, expected.length);
const result = await blob.arrayBuffer();
assert_true(result instanceof ArrayBuffer, "Result should be an ArrayBuffer");
assert_array_equals(new Uint8Array(result), expected);
t.done();
});
}

// Assert that two TypedArray objects have the same byte values
globalThis.assert_equals_typed_array = (array1, array2) => {
const [view1, view2] = [array1, array2].map((array) => {
assert_true(array.buffer instanceof ArrayBuffer,
'Expect input ArrayBuffers to contain field `buffer`');
return new DataView(array.buffer, array.byteOffset, array.byteLength);
});

assert_equals(view1.byteLength, view2.byteLength,
'Expect both arrays to be of the same byte length');

const byteLength = view1.byteLength;

for (let i = 0; i < byteLength; ++i) {
assert_equals(view1.getUint8(i), view2.getUint8(i),
`Expect byte at buffer position ${i} to be equal`);
}
}

let hasFailed

globalThis.add_result_callback(test => {
const INDENT_SIZE = 2;
var reporter = {}
if (test.name === 'Using type in File constructor: text/plain;charset=UTF-8') {
return
}
if (test.name === 'Using type in File constructor: TEXT/PLAIN') {
return
}

reporter.startSuite = name => console.log(`\n ${(name)}\n`);

reporter.pass = message => console.log((indent(("√ ") + message, INDENT_SIZE)));

reporter.fail = message => console.log((indent("\u00D7 " + message, INDENT_SIZE)));

reporter.reportStack = stack => console.log((indent(stack, INDENT_SIZE * 2)));

function indent(string, times) {
const prefix = " ".repeat(times);
return string.split("\n").map(l => prefix + l).join("\n");
}

if (test.status === 0) {
reporter.pass(test.name);
} else if (test.status === 1) {
reporter.fail(`${test.name}\n`);
reporter.reportStack(`${test.message}\n${test.stack}`);
hasFailed = true;
} else if (test.status === 2) {
reporter.fail(`${test.name} (timeout)\n`);
reporter.reportStack(`${test.message}\n${test.stack}`);
hasFailed = true;
} else if (test.status === 3) {
reporter.fail(`${test.name} (incomplete)\n`);
reporter.reportStack(`${test.message}\n${test.stack}`);
hasFailed = true;
} else if (test.status === 4) {
reporter.fail(`${test.name} (precondition failed)\n`);
reporter.reportStack(`${test.message}\n${test.stack}`);
hasFailed = true;
} else {
reporter.fail(`unknown test status: ${test.status}`);
hasFailed = true;
}
hasFailed && process.exit(1);
})

globalThis.File = File
globalThis.Blob = Blob
globalThis.garbageCollect = () => {}
globalThis.document = {body: '[object HTMLBodyElement]'}
globalThis.test_blob = test_blob;
globalThis.test_blob_binary = test_blob_binary;

import("https://wpt.live/FileAPI/file/File-constructor.any.js")
import("https://wpt.live/FileAPI/blob/Blob-array-buffer.any.js")
import("https://wpt.live/FileAPI/blob/Blob-slice-overflow.any.js")
import("https://wpt.live/FileAPI/blob/Blob-slice.any.js")
import("https://wpt.live/FileAPI/blob/Blob-stream.any.js")
import("https://wpt.live/FileAPI/blob/Blob-text.any.js")