Skip to content

Commit 9b8372b

Browse files
authored
fix(fetch-http-handler): handle empty blob in browsers (#1199)
* fix(fetch-http-handler): handle empty blob in browsers * test: add test for handling null FileReader result * chore: rename karma tests to *.browser.spec.ts
1 parent 4b2c17b commit 9b8372b

File tree

5 files changed

+76
-25
lines changed

5 files changed

+76
-25
lines changed

packages/fetch-http-handler/jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ const base = require("../../jest.config.base.js");
22

33
module.exports = {
44
...base,
5-
testPathIgnorePatterns: ["/node_modules/", "stream-collector.spec.js"]
5+
testPathIgnorePatterns: ["/node_modules/", "(.*).browser.spec.js"]
66
};

packages/fetch-http-handler/karma.conf.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ process.env.CHROME_BIN = require("puppeteer").executablePath();
22
module.exports = function (config) {
33
config.set({
44
frameworks: ["jasmine", "karma-typescript"],
5-
files: ["src/stream-collector*.ts"],
5+
files: ["src/stream-collector.ts", "src/stream-collector.browser.spec.ts"],
66
exclude: ["**/*.d.ts"],
77
preprocessors: {
88
"**/*.ts": "karma-typescript"
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { streamCollector } from "./stream-collector";
2+
3+
describe("streamCollector", () => {
4+
it("returns a Uint8Array from a blob", done => {
5+
const expected = Uint8Array.from([102, 111, 111]);
6+
const dataPromise = new Response(expected.buffer)
7+
.blob()
8+
.then(blob => streamCollector(blob));
9+
dataPromise.then((data: any) => {
10+
expect(data).toEqual(expected);
11+
done();
12+
});
13+
});
14+
15+
it("returns a Uint8Array from a ReadableStream", done => {
16+
const expected = Uint8Array.from([102, 111, 111]);
17+
const dataPromise = streamCollector(new Response(expected.buffer).body);
18+
dataPromise.then((data: any) => {
19+
expect(data).toEqual(expected);
20+
done();
21+
});
22+
});
23+
24+
it("returns a Uint8Array when stream is empty", done => {
25+
const expected = new Uint8Array(0);
26+
const dataPromise = streamCollector(new Response(expected.buffer).body);
27+
dataPromise.then((data: any) => {
28+
expect(data).toEqual(expected);
29+
done();
30+
});
31+
});
32+
33+
it("returns a Uint8Array when blob is empty", done => {
34+
const expected = new Uint8Array(0);
35+
36+
const dataPromise = new Response(expected.buffer)
37+
.blob()
38+
.then(blob => streamCollector(blob));
39+
dataPromise.then((data: any) => {
40+
expect(data).toEqual(expected);
41+
done();
42+
});
43+
});
44+
});

packages/fetch-http-handler/src/stream-collector.spec.ts

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,35 @@
11
import { streamCollector } from "./stream-collector";
22

3+
/**
4+
* Have to mock the FileReader behavior in IE, where
5+
* reader.result is null if reads an empty blob.
6+
*/
37
describe("streamCollector", () => {
4-
it("returns a Uint8Array from a blob", done => {
5-
const dataPromise = new Response(new Uint8Array([102, 111, 111]).buffer)
6-
.blob()
7-
.then(blob => streamCollector(blob));
8-
dataPromise.then((data: any) => {
9-
expect(data).toEqual(Uint8Array.from([102, 111, 111]));
10-
done();
11-
});
8+
let originalFileReader = (global as any).FileReader;
9+
let originalBlob = (global as any).Blob;
10+
beforeAll(() => {
11+
originalFileReader = (global as any).FileReader;
12+
originalBlob = (global as any).Blob;
1213
});
13-
14-
it("returns a Uint8Array from a ReadableStream", done => {
15-
const dataPromise = streamCollector(
16-
new Response(new Uint8Array([102, 111, 111]).buffer).body
17-
);
18-
dataPromise.then((data: any) => {
19-
expect(data).toEqual(Uint8Array.from([102, 111, 111]));
20-
done();
21-
});
14+
afterAll(() => {
15+
(global as any).FileReader = originalFileReader;
16+
(global as any).Blob = originalBlob;
2217
});
2318

24-
it("returns a Uint8Array when stream is empty", done => {
25-
const expected = new Uint8Array(0);
26-
const dataPromise = streamCollector(new Response(expected.buffer).body);
19+
it("returns a Uint8Array when blob is empty and when FileReader data is null(in IE)", done => {
20+
(global as any).FileReader = function FileReader() {
21+
this.result = null; //In IE, FileReader.result is null after reading empty blob
22+
this.readAsDataURL = jest.fn().mockImplementation(() => {
23+
if (this.onloadend) {
24+
this.readyState = 2;
25+
this.onloadend();
26+
}
27+
});
28+
};
29+
(global as any).Blob = function Blob() {};
30+
const dataPromise = streamCollector(new Blob());
2731
dataPromise.then((data: any) => {
28-
expect(data).toEqual(expected);
32+
expect(data).toEqual(Uint8Array.from([]));
2933
done();
3034
});
3135
});

packages/fetch-http-handler/src/stream-collector.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,11 @@ function readToBase64(blob: Blob): Promise<string> {
4444
if (reader.readyState !== 2) {
4545
return reject(new Error("Reader aborted too early"));
4646
}
47-
const result = reader.result as string;
48-
const dataOffset = result.indexOf(",") + 1;
47+
const result = (reader.result ?? "") as string;
48+
// Response can include only 'data:' for empty blob, return empty string in this case.
49+
// Otherwise, return the string after ','
50+
const commaIndex = result.indexOf(",");
51+
const dataOffset = commaIndex > -1 ? commaIndex + 1 : result.length;
4952
resolve(result.substring(dataOffset));
5053
};
5154
reader.onabort = () => reject(new Error("Read aborted"));

0 commit comments

Comments
 (0)