up-fetch
Version:
Advanced fetch client builder for typescript.
176 lines (164 loc) • 5.06 kB
text/typescript
import { http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'
import { up } from 'src/up'
import { afterAll, afterEach, beforeAll, expect, test, vi } from 'vitest'
const baseUrl = 'http://a.b.c'
const encoder = new TextEncoder()
const server = setupServer(
http.get(`${baseUrl}/empty`, () => {
return new HttpResponse(null, {})
}),
http.get(`${baseUrl}/chatbot`, () => {
const stream = new ReadableStream({
start(controller) {
// Encode the string chunks using "TextEncoder".
controller.enqueue(encoder.encode('Brand'))
controller.enqueue(encoder.encode('New'))
controller.enqueue(encoder.encode('World'))
controller.close()
},
})
// Send the mocked response immediately.
return new HttpResponse(stream, {
headers: {
'Content-Type': 'text/plain',
'Content-Length': 'BrandNewWorld'.length.toString(),
},
})
}),
http.get(`${baseUrl}/chatbot-nocontentlength`, () => {
const stream = new ReadableStream({
start(controller) {
// Encode the string chunks using "TextEncoder".
controller.enqueue(encoder.encode('Brand'))
controller.enqueue(encoder.encode('New'))
controller.enqueue(encoder.encode('World'))
controller.close()
},
})
// Send the mocked response immediately.
return new HttpResponse(stream, {
headers: { 'Content-Type': 'text/plain' },
})
}),
http.get(`${baseUrl}/nostream`, () => {
return HttpResponse.text('hello', {
status: 200,
statusText: 'status text',
headers: { some: 'header' },
})
}),
)
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
test('should not call onResponseStreaming when body is empty', async () => {
const upfetch = up(fetch)
const spy = vi.fn()
const data = await upfetch(`${baseUrl}/empty`, {
onResponseStreaming(event) {
spy(event)
},
})
expect(data).toBe(null)
expect(spy).not.toHaveBeenCalled()
})
test("should infer totalBytes from the 'Content-Length' header", async () => {
const upfetch = up(fetch)
const spy = vi.fn()
await upfetch(`${baseUrl}/chatbot`, {
onResponseStreaming(event) {
spy(event)
},
})
expect(spy).toHaveBeenCalledWith({
totalBytes: 13,
transferredBytes: 0,
chunk: new Uint8Array(),
})
})
test('should call onResponseStreaming for each chunk', async () => {
const upfetch = up(fetch)
const spy = vi.fn()
const data = await upfetch(`${baseUrl}/chatbot`, {
onResponseStreaming(event) {
spy(event)
},
})
expect(data).toEqual('BrandNewWorld')
expect(spy).toHaveBeenNthCalledWith(1, {
totalBytes: 13,
transferredBytes: 0,
chunk: new Uint8Array(),
})
expect(spy).toHaveBeenNthCalledWith(2, {
totalBytes: 13,
transferredBytes: 5,
chunk: expect.any(Uint8Array),
})
expect(spy).toHaveBeenNthCalledWith(3, {
totalBytes: 13,
transferredBytes: 8,
chunk: expect.any(Uint8Array),
})
expect(spy).toHaveBeenNthCalledWith(4, {
totalBytes: 13,
transferredBytes: 13,
chunk: expect.any(Uint8Array),
})
})
test("should infer totalBytes from the transferredBytes if no 'Content-Length' header is present", async () => {
const upfetch = up(fetch)
const spy = vi.fn()
const data = await upfetch(`${baseUrl}/chatbot-nocontentlength`, {
onResponseStreaming(event) {
spy(event)
},
})
expect(data).toEqual('BrandNewWorld')
expect(spy).toHaveBeenNthCalledWith(1, {
totalBytes: 0,
transferredBytes: 0,
chunk: new Uint8Array(),
})
expect(spy).toHaveBeenNthCalledWith(2, {
totalBytes: 5,
transferredBytes: 5,
chunk: expect.any(Uint8Array),
})
expect(spy).toHaveBeenNthCalledWith(3, {
totalBytes: 8,
transferredBytes: 8,
chunk: expect.any(Uint8Array),
})
expect(spy).toHaveBeenNthCalledWith(4, {
totalBytes: 13,
transferredBytes: 13,
chunk: expect.any(Uint8Array),
})
})
test('should work with normal endpoints', async () => {
const upfetch = up(fetch)
const spy = vi.fn()
const data = await upfetch(`${baseUrl}/nostream`, {
onResponseStreaming(event) {
spy(event)
},
})
expect(data).toEqual('hello')
expect(spy).toHaveBeenCalledWith({
totalBytes: 5,
transferredBytes: 5,
chunk: expect.any(Uint8Array),
})
})
test('should preserve headers, status, and statusText', async () => {
const upfetch = up(fetch)
const response = await upfetch(`${baseUrl}/nostream`, {
parseResponse: (res) => res,
onResponseStreaming() {},
})
expect(response.status).toEqual(200)
expect(response.statusText).toEqual('status text')
expect(response.headers.get('some')).toEqual('header')
})