text() method not available in Blob
Asked Answered
T

2

5

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.

Tetrasyllable answered 16/12, 2020 at 17:26 Comment(11)
Where are you running this code? The text method of Blob is not universally supported.Probity
I am running this code on my local machine, through VS Code. When I run my code in real, it works well (i.e.: without addMissingTextPropertyToBlob) on both Chrome, Edge & Firefox.Tetrasyllable
VS Code doesn't run code by itself. Where does VS Code run the code?Probity
Sorry: to launch my tests, I execute yarn test (i.e.: npx jest) in the VS Code's terminal of my machine (Windows 10 Pro)Tetrasyllable
And what does the test script in your package.json do?Probity
It does "test": "jest"Tetrasyllable
And jest will run the foo.test.js in a node.js environment, not in a browser?!Probity
Euh yes ^^, should I conclude that it behaves as expected? I am using nock for HTTP server mocking. Shouldn't axios or nock provide the proper blob API when I receive the response?Tetrasyllable
If you're using Nock, I assume you're running this in Node. keep in mind that Node doesn't have native support for Blob. So Axios is doing something funky to mimic a Blob when responseType: 'blob' is provided.Benempt
OK, so using an interceptor like addMissingTextPropertyToBlob is the right way to correctly set up my tests. Thanks. I let you write the proper answer, otherwise I can/will do it myself!Tetrasyllable
…or you just open a feature request with that nock library to implement the full API. Which, as you demonstrated, isn't hard.Probity
A
9

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';

Agnostic answered 22/4, 2022 at 18:40 Comment(0)
K
3

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;
Kamin answered 20/12, 2023 at 10:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.