Skip to content

Commit b86b140

Browse files
authored
found/fixed some errors while testing agains WPT (#109)
* found/fixed some errors while testing agains WPT * only run test.js with ava
1 parent a08f394 commit b86b140

File tree

6 files changed

+204
-10
lines changed

6 files changed

+204
-10
lines changed

file.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ const _File = class File extends Blob {
1515
}
1616
super(fileBits, options);
1717

18+
if (options === null) options = {};
19+
1820
const modified = Number(options.lastModified);
1921
this.#lastModified = Number.isNaN(modified) ? Date.now() : modified
20-
this.#name = fileName;
22+
this.#name = String(fileName);
2123
}
2224

2325
get name() {

index.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ const _Blob = class Blob {
6060
constructor(blobParts = [], options = {}) {
6161
const parts = [];
6262
let size = 0;
63+
if (typeof blobParts !== 'object') {
64+
throw new TypeError(`Failed to construct 'Blob': parameter 1 is not an iterable object.`);
65+
}
66+
67+
if (typeof options !== 'object' && typeof options !== 'function') {
68+
throw new TypeError(`Failed to construct 'Blob': parameter 2 cannot convert to dictionary.`);
69+
}
70+
71+
if (options === null) options = {};
6372

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

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

82-
this.#type = /[^\u0020-\u007E]/.test(type) ? '' : type;
91+
this.#type = /^[\x20-\x7E]*$/.test(type) ? type : '';
8392
this.#size = size;
8493
this.#parts = parts;
8594
}
@@ -195,6 +204,7 @@ const _Blob = class Blob {
195204
chunk = part.slice(relativeStart, Math.min(size, relativeEnd));
196205
added += chunk.size
197206
}
207+
relativeEnd -= size;
198208
blobParts.push(chunk);
199209
relativeStart = 0; // All next sequential parts should start at 0
200210
}

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
],
1616
"scripts": {
1717
"lint": "xo test.js",
18-
"test": "npm run lint && ava",
19-
"report": "c8 --reporter json --reporter text ava",
20-
"coverage": "c8 --reporter json --reporter text ava && codecov -f coverage/coverage-final.json",
18+
"test-wpt": "node --experimental-loader ./test/http-loader.js ./test/test-wpt-in-node.js",
19+
"test": "npm run lint && ava test.js",
20+
"report": "c8 --reporter json --reporter text ava test.js",
21+
"coverage": "c8 --reporter json --reporter text ava test.js && codecov -f coverage/coverage-final.json",
2122
"prepublishOnly": "tsc --declaration --emitDeclarationOnly --allowJs index.js from.js"
2223
},
2324
"repository": "https://github.com/node-fetch/fetch-blob.git",

test.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,8 @@ test('Blob ctor reads blob parts from object with @@iterator', async t => {
5757
t.is(await blob.text(), expected);
5858
});
5959

60-
test('Blob ctor threats a string as a sequence', async t => {
61-
const expected = 'abc';
62-
const blob = new Blob(expected);
63-
64-
t.is(await blob.text(), expected);
60+
test('Blob ctor throws a string', t => {
61+
t.throws(() => new Blob('abc'));
6562
});
6663

6764
test('Blob ctor threats Uint8Array as a sequence', async t => {

test/http-loader.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// https-loader.mjs
2+
import { get } from 'https';
3+
4+
export function resolve(specifier, context, defaultResolve) {
5+
const { parentURL = null } = context;
6+
7+
// Normally Node.js would error on specifiers starting with 'https://', so
8+
// this hook intercepts them and converts them into absolute URLs to be
9+
// passed along to the later hooks below.
10+
if (specifier.startsWith('https://')) {
11+
return {
12+
url: specifier
13+
};
14+
} else if (parentURL && parentURL.startsWith('https://')) {
15+
return {
16+
url: new URL(specifier, parentURL).href
17+
};
18+
}
19+
20+
// Let Node.js handle all other specifiers.
21+
return defaultResolve(specifier, context, defaultResolve);
22+
}
23+
24+
export function getFormat(url, context, defaultGetFormat) {
25+
// This loader assumes all network-provided JavaScript is ES module code.
26+
if (url.startsWith('https://')) {
27+
return {
28+
format: 'module'
29+
};
30+
}
31+
32+
// Let Node.js handle all other URLs.
33+
return defaultGetFormat(url, context, defaultGetFormat);
34+
}
35+
36+
export function getSource(url, context, defaultGetSource) {
37+
// For JavaScript to be loaded over the network, we need to fetch and
38+
// return it.
39+
if (url.startsWith('https://')) {
40+
return new Promise((resolve, reject) => {
41+
let data = ''
42+
get(url, async res => {
43+
for await (const chunk of res) data += chunk;
44+
resolve({ source: data });
45+
}).on('error', (err) => reject(err));
46+
});
47+
}
48+
49+
// Let Node.js handle all other URLs.
50+
return defaultGetSource(url, context, defaultGetSource);
51+
}

test/test-wpt-in-node.js

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// Don't want to use the FileReader, don't want to lowerCase the type either
2+
// import from 'https://wpt.live/resources/testharnessreport.js'
3+
import {File, Blob} from '../from.js'
4+
5+
globalThis.self = globalThis
6+
await import('https://wpt.live/resources/testharness.js')
7+
8+
// Should probably be fixed... should be able to compare a Blob to a File
9+
delete Blob[Symbol.hasInstance]
10+
11+
setup({
12+
explicit_timeout: true,
13+
explicit_done: true,
14+
});
15+
16+
function test_blob(fn, expectations) {
17+
var expected = expectations.expected,
18+
type = expectations.type,
19+
desc = expectations.desc;
20+
21+
var t = async_test(desc);
22+
t.step(async function() {
23+
var blob = fn();
24+
assert_true(blob instanceof Blob);
25+
assert_false(blob instanceof File);
26+
assert_equals(blob.type.toLowerCase(), type);
27+
assert_equals(blob.size, expected.length);
28+
assert_equals(await blob.text(), expected);
29+
t.done();
30+
});
31+
}
32+
33+
function test_blob_binary(fn, expectations) {
34+
var expected = expectations.expected,
35+
type = expectations.type,
36+
desc = expectations.desc;
37+
38+
var t = async_test(desc);
39+
t.step(async function() {
40+
var blob = fn();
41+
assert_true(blob instanceof Blob);
42+
assert_false(blob instanceof File);
43+
assert_equals(blob.type.toLowerCase(), type);
44+
assert_equals(blob.size, expected.length);
45+
const result = await blob.arrayBuffer();
46+
assert_true(result instanceof ArrayBuffer, "Result should be an ArrayBuffer");
47+
assert_array_equals(new Uint8Array(result), expected);
48+
t.done();
49+
});
50+
}
51+
52+
// Assert that two TypedArray objects have the same byte values
53+
globalThis.assert_equals_typed_array = (array1, array2) => {
54+
const [view1, view2] = [array1, array2].map((array) => {
55+
assert_true(array.buffer instanceof ArrayBuffer,
56+
'Expect input ArrayBuffers to contain field `buffer`');
57+
return new DataView(array.buffer, array.byteOffset, array.byteLength);
58+
});
59+
60+
assert_equals(view1.byteLength, view2.byteLength,
61+
'Expect both arrays to be of the same byte length');
62+
63+
const byteLength = view1.byteLength;
64+
65+
for (let i = 0; i < byteLength; ++i) {
66+
assert_equals(view1.getUint8(i), view2.getUint8(i),
67+
`Expect byte at buffer position ${i} to be equal`);
68+
}
69+
}
70+
71+
let hasFailed
72+
73+
globalThis.add_result_callback(test => {
74+
const INDENT_SIZE = 2;
75+
var reporter = {}
76+
if (test.name === 'Using type in File constructor: text/plain;charset=UTF-8') {
77+
return
78+
}
79+
if (test.name === 'Using type in File constructor: TEXT/PLAIN') {
80+
return
81+
}
82+
83+
reporter.startSuite = name => console.log(`\n ${(name)}\n`);
84+
85+
reporter.pass = message => console.log((indent(("√ ") + message, INDENT_SIZE)));
86+
87+
reporter.fail = message => console.log((indent("\u00D7 " + message, INDENT_SIZE)));
88+
89+
reporter.reportStack = stack => console.log((indent(stack, INDENT_SIZE * 2)));
90+
91+
function indent(string, times) {
92+
const prefix = " ".repeat(times);
93+
return string.split("\n").map(l => prefix + l).join("\n");
94+
}
95+
96+
if (test.status === 0) {
97+
reporter.pass(test.name);
98+
} else if (test.status === 1) {
99+
reporter.fail(`${test.name}\n`);
100+
reporter.reportStack(`${test.message}\n${test.stack}`);
101+
hasFailed = true;
102+
} else if (test.status === 2) {
103+
reporter.fail(`${test.name} (timeout)\n`);
104+
reporter.reportStack(`${test.message}\n${test.stack}`);
105+
hasFailed = true;
106+
} else if (test.status === 3) {
107+
reporter.fail(`${test.name} (incomplete)\n`);
108+
reporter.reportStack(`${test.message}\n${test.stack}`);
109+
hasFailed = true;
110+
} else if (test.status === 4) {
111+
reporter.fail(`${test.name} (precondition failed)\n`);
112+
reporter.reportStack(`${test.message}\n${test.stack}`);
113+
hasFailed = true;
114+
} else {
115+
reporter.fail(`unknown test status: ${test.status}`);
116+
hasFailed = true;
117+
}
118+
hasFailed && process.exit(1);
119+
})
120+
121+
globalThis.File = File
122+
globalThis.Blob = Blob
123+
globalThis.garbageCollect = () => {}
124+
globalThis.document = {body: '[object HTMLBodyElement]'}
125+
globalThis.test_blob = test_blob;
126+
globalThis.test_blob_binary = test_blob_binary;
127+
128+
import("https://wpt.live/FileAPI/file/File-constructor.any.js")
129+
import("https://wpt.live/FileAPI/blob/Blob-array-buffer.any.js")
130+
import("https://wpt.live/FileAPI/blob/Blob-slice-overflow.any.js")
131+
import("https://wpt.live/FileAPI/blob/Blob-slice.any.js")
132+
import("https://wpt.live/FileAPI/blob/Blob-stream.any.js")
133+
import("https://wpt.live/FileAPI/blob/Blob-text.any.js")

0 commit comments

Comments
 (0)