Skip to content

Commit

Permalink
fix(XMLHttpRequest): return ArrayBuffer for "arraybuffer" response types
Browse files Browse the repository at this point in the history
  • Loading branch information
kettanaito committed Oct 13, 2022
1 parent f106377 commit d52d71d
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 7 deletions.
8 changes: 6 additions & 2 deletions src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import { Request } from '@remix-run/web-fetch'
import { parseJson } from '../../utils/parseJson'
import { createEvent } from './utils/createEvent'
import type { XMLHttpRequestEmitter } from '.'
import { encodeBuffer, decodeBuffer } from '../../utils/bufferUtils'
import {
encodeBuffer,
decodeBuffer,
toArrayBuffer,
} from '../../utils/bufferUtils'
import { createResponse } from './utils/createResponse'
import { concatArrayBuffer } from './utils/concatArrayBuffer'
import { toInteractiveRequest } from '../../utils/toInteractiveRequest'
Expand Down Expand Up @@ -490,7 +494,7 @@ export const createXMLHttpRequestOverride = (

case 'arraybuffer': {
this.log('resolving response body as ArrayBuffer')
return this._responseBuffer
return toArrayBuffer(this._responseBuffer)
}

case 'blob': {
Expand Down
12 changes: 12 additions & 0 deletions src/utils/bufferUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,15 @@ export function decodeBuffer(buffer: ArrayBuffer, encoding?: string): string {
const decoder = new TextDecoder(encoding)
return decoder.decode(buffer)
}

/**
* Create an `ArrayBuffer` from the given `Uint8Array`.
* Takes the byte offset into account to produce the right buffer
* in the case when the buffer is bigger than the data view.
*/
export function toArrayBuffer(array: Uint8Array): ArrayBuffer {
return array.buffer.slice(
array.byteOffset,
array.byteOffset + array.byteLength
)
}
2 changes: 1 addition & 1 deletion src/utils/parseJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Parses a given string into JSON.
* Gracefully handles invalid JSON by returning `null`.
*/
export function parseJson(data: string): Record<string, any> | null {
export function parseJson(data: string): Record<string, unknown> | null {
try {
const json = JSON.parse(data)
return json
Expand Down
19 changes: 15 additions & 4 deletions test/modules/XMLHttpRequest/compliance/xhr-response-type.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
* @jest-environment jsdom
*/
import { Response } from '@remix-run/web-fetch'
import { encodeBuffer } from '../../../../src'
import { XMLHttpRequestInterceptor } from '../../../../src/interceptors/XMLHttpRequest'
import { toArrayBuffer } from '../../../../src/utils/bufferUtils'
import { createXMLHttpRequest, readBlob } from '../../../helpers'

const interceptor = new XMLHttpRequestInterceptor()
Expand Down Expand Up @@ -85,16 +87,25 @@ test('responds with an ArrayBuffer when "responseType" equals "arraybuffer"', as
req.send()
})

const expectedArrayBuffer = new Uint8Array(
Buffer.from(
const expectedArrayBuffer = toArrayBuffer(
encodeBuffer(
JSON.stringify({
firstName: 'John',
lastName: 'Maverick',
})
)
)

const responseBuffer: Uint8Array = req.response
const responseBuffer = req.response as ArrayBuffer

expect(Buffer.compare(responseBuffer, expectedArrayBuffer)).toBe(0)
const isBufferEqual = (left: ArrayBuffer, right: ArrayBuffer): boolean => {
const first = new Uint8Array(left)
const last = new Uint8Array(right)
return first.every((value, index) => last[index] === value)
}

// Must return an "ArrayBuffer" instance for "arraybuffer" response type.
expect(responseBuffer).toBeInstanceOf(ArrayBuffer)
expect(responseBuffer.byteLength).toBe(expectedArrayBuffer.byteLength)
expect(isBufferEqual(responseBuffer, expectedArrayBuffer)).toBe(true)
})

0 comments on commit d52d71d

Please sign in to comment.