Skip to content

Commit

Permalink
Support raw binary responses
Browse files Browse the repository at this point in the history
Fix sinonjs/sinon#1570

Binary strings will be encoded using utf-8, instead of being returned
unchanged in the response body. This commit allows binary response
data to be passed as an array buffer directly to nise, while keeping the
existing behavior of encoding normal strings in utf-8 as discussed in
sinonjs/sinon#875 (comment).
  • Loading branch information
tzrh committed Sep 27, 2017
1 parent 8a7f05d commit 1663407
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 13 deletions.
28 changes: 23 additions & 5 deletions lib/fake-xhr/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,16 +224,34 @@ function verifyHeadersReceived(xhr) {
}
}

function verifyResponseBodyType(body) {
if (typeof body !== "string") {
var error = new Error("Attempted to respond to fake XMLHttpRequest with " +
body + ", which is not a string.");
function verifyResponseBodyType(body, responseType) {
var error = null;
var isString = typeof body === "string";

if (responseType === "arraybuffer") {

if (!isString && !(body instanceof ArrayBuffer)) {
error = new Error("Attempted to respond to fake XMLHttpRequest with " +
body + ", which is not a string or ArrayBuffer.");
error.name = "InvalidBodyException";
}
}
else if (!isString) {
error = new Error("Attempted to respond to fake XMLHttpRequest with " +
body + ", which is not a string.");
error.name = "InvalidBodyException";
}

if (error) {
throw error;
}
}

function convertToArrayBuffer(body, encoding) {
if (body instanceof ArrayBuffer) {
return body;
}

return new TextEncoder(encoding || "utf-8").encode(body).buffer;
}

Expand Down Expand Up @@ -576,7 +594,7 @@ extend(FakeXMLHttpRequest.prototype, sinonEvent.EventTarget, {
setResponseBody: function setResponseBody(body) {
verifyRequestSent(this);
verifyHeadersReceived(this);
verifyResponseBodyType(body);
verifyResponseBodyType(body, this.responseType);
var contentType = this.overriddenMimeType || this.getResponseHeader("Content-Type");

var isTextResponse = this.responseType === "" || this.responseType === "text";
Expand Down
37 changes: 29 additions & 8 deletions lib/fake-xhr/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ var sinonSandbox = require("sinon").sandbox;
var sinon = require("sinon");
var extend = require("just-extend");

var TextDecoder = global.TextDecoder || require("text-encoding").TextDecoder;
var TextEncoder = global.TextEncoder || require("text-encoding").TextEncoder;
var DOMParser = require("xmldom").DOMParser;
var assert = referee.assert;
var refute = referee.refute;
Expand Down Expand Up @@ -46,10 +46,16 @@ function runWithWorkingXHROveride(workingXHR, test) {
}
}

function assertArrayBufferMatches(actual, expected, encoding) {
function toBinaryString(buffer) {
return String.fromCharCode.apply(null, new Uint8Array(buffer));
}

function assertArrayBufferMatches(actual, expected) {
assert(actual instanceof ArrayBuffer, "${0} expected to be an ArrayBuffer");
var actualString = new TextDecoder(encoding || "utf-8").decode(actual);
assert.same(actualString, expected, "ArrayBuffer [${0}] expected to match ArrayBuffer [${1}]");
assert(expected instanceof ArrayBuffer, "${0} expected to be an ArrayBuffer");
var actualString = toBinaryString(actual);
var expectedString = toBinaryString(expected);
assert.same(actualString, expectedString, "ArrayBuffer [${0}] expected to match ArrayBuffer [${1}]");
}

function assertBlobMatches(actual, expected, done) {
Expand Down Expand Up @@ -1613,7 +1619,7 @@ describe("FakeXMLHttpRequest", function () {
this.xhr.send();

this.xhr.respond();
assertArrayBufferMatches(this.xhr.response, "");
assertArrayBufferMatches(this.xhr.response, new Uint8Array([]).buffer);
});

it("returns ArrayBuffer when responseType='arraybuffer'", function () {
Expand All @@ -1623,17 +1629,32 @@ describe("FakeXMLHttpRequest", function () {

this.xhr.respond(200, { "Content-Type": "application/octet-stream" }, "a test buffer");

assertArrayBufferMatches(this.xhr.response, "a test buffer");
var expected = new TextEncoder("utf-8").encode("a test buffer").buffer;
assertArrayBufferMatches(this.xhr.response, expected);
});

it("returns binary data correctly when responseType='arraybuffer'", function () {
it("returns utf-8 strings correctly when responseType='arraybuffer'", function () {
this.xhr.responseType = "arraybuffer";
this.xhr.open("GET", "/");
this.xhr.send();

this.xhr.respond(200, { "Content-Type": "application/octet-stream" }, "\xFF");

assertArrayBufferMatches(this.xhr.response, "\xFF");
var expectedBuffer = new TextEncoder("utf-8").encode("\xFF").buffer;

assertArrayBufferMatches(this.xhr.response, expectedBuffer);
});

it("returns binary data correctly when responseType='arraybuffer'", function () {
this.xhr.responseType = "arraybuffer";
this.xhr.open("GET", "/");
this.xhr.send();

var buffer = new Uint8Array([160, 64, 0, 0, 32, 193]).buffer;

this.xhr.respond(200, { "Content-Type": "application/octet-stream" }, buffer);

assertArrayBufferMatches(this.xhr.response, buffer);
});
});
}
Expand Down

1 comment on commit 1663407

@fatso83
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, this looks great. Want to make it into a pull request so that we can merge it?

Please sign in to comment.