text() method not available in Blob

3k views Asked by At

I am doing some Integration tests with jest & nock to test Axios interceptors chaining.
In one of them, I am expecting to receive a Blob instance
and according to this link, I should be able to use its text() method.
My question is: Why is this method not defined? (in my tests)

Here is the (simplified) code:

// interceptors.js
const useBlobTextProperty = async (response) => {
  const { data: myBlob } = response
  console.log(myBlob.toString())          // Prints "[object Blob]"
  response.rawText = await myBlob.text()  // TypeError: myBlob.text is not a function
  return response
}
// foo.test.js
import axios from 'axios'
import httpAdapter from 'axios/lib/adapters/http'
import nock from 'nock'

const testAxios = axios.create({
  responseEncoding: 'utf8',
  responseType: 'blob',
})
testAxios.interceptors.response.use(useBlobTextProperty, null)

const host = 'http://localhost'

axios.defaults.host = host
axios.defaults.adapter = httpAdapter

describe('SO test', () => {
  beforeEach(() => {
    nock(host)
      .defaultReplyHeaders({ 'Content-Type': 'application/json' })
      .persist()
      .get('/')
      .reply(200, { foo: 17 })
  })
  afterEach(() => {
    nock.cleanAll()
  })

  it('should get raw text', async () => {
    const returnValue = await testAxios.get('/') // FAIL: TypeError
    expect(returnValue.rawText).toEqual('{"foo":17}')
  }, 1000)
})

FYI, to workaround this issue, I am using another interceptor:

// interceptors.js
const addMissingTextPropertyToBlob = (response) => {
  const { data } = response
  if (data.toString() === '[object Blob]' && !data.text) {
    data.text = () =>
      new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.onload = () => { resolve(reader.result) }
        reader.onerror = (error) => { reject(error) }
        reader.readAsText(data)
      })
  }
  return response
}
// foo.test.js
// Response interceptors are executed from index 0 to N-1
testAxios.interceptors.response.use(addMissingTextPropertyToBlob, null)
testAxios.interceptors.response.use(useBlobTextProperty, null)
// Then, it works!

But I'd like to understand why I cannot rely on Blob.text() in my tests.

2

There are 2 answers

0
Washington Braga On

In my case, this approach worked for me:

yarn add blob-polyfill -D

and then import the following in your jest setupTests:

import 'blob-polyfill';

0
Sergei Solovev On

The problem

The problem comes from the Blob issue in JSDom which Jest uses under the hood as an environment when running your tests. JSDom's Blob implementation has no support for text, stream and arrayBuffer methods.

The solution

So, you can use blob-polyfill library as @washington-braga said. Also there is one more option with no external dependencies. You can override JSDom version of Blob with native Node.js one like so

import {Blob as BlobPolyfill} from 'node:buffer';

global.Blob = BlobPolyfill as any;

I checked it works with Node 18.18.2.

If you use plain JavaScript, I think this code should help (I didn't check)

global.Blob = require('node:buffer').Blob;