summaryrefslogtreecommitdiffstats
path: root/test/jest
diff options
context:
space:
mode:
Diffstat (limited to 'test/jest')
-rw-r--r--test/jest/instanceof-error.test.js44
-rw-r--r--test/jest/interceptor.test.js197
-rw-r--r--test/jest/issue-1757.test.js61
-rw-r--r--test/jest/mock-agent.test.js46
-rw-r--r--test/jest/mock-scope.test.js32
-rw-r--r--test/jest/test.js36
6 files changed, 416 insertions, 0 deletions
diff --git a/test/jest/instanceof-error.test.js b/test/jest/instanceof-error.test.js
new file mode 100644
index 0000000..8bb36d2
--- /dev/null
+++ b/test/jest/instanceof-error.test.js
@@ -0,0 +1,44 @@
+'use strict'
+
+const { createServer } = require('http')
+const { once } = require('events')
+
+/* global expect, it, jest, AbortController */
+
+// https://github.com/facebook/jest/issues/11607#issuecomment-899068995
+jest.useRealTimers()
+
+const runIf = (condition) => condition ? it : it.skip
+const nodeMajor = Number(process.versions.node.split('.', 1)[0])
+
+runIf(nodeMajor >= 16)('isErrorLike sanity check', () => {
+ const { isErrorLike } = require('../../lib/fetch/util')
+ const { DOMException } = require('../../lib/fetch/constants')
+ const error = new DOMException('')
+
+ // https://github.com/facebook/jest/issues/2549
+ expect(error instanceof Error).toBeFalsy()
+ expect(isErrorLike(error)).toBeTruthy()
+})
+
+runIf(nodeMajor >= 16)('Real use-case', async () => {
+ const { fetch } = require('../..')
+
+ const ac = new AbortController()
+ ac.abort()
+
+ const server = createServer((req, res) => {
+ res.end()
+ }).listen(0)
+
+ await once(server, 'listening')
+
+ const promise = fetch(`https://localhost:${server.address().port}`, {
+ signal: ac.signal
+ })
+
+ await expect(promise).rejects.toThrowError(/^Th(e|is) operation was aborted\.?$/)
+
+ server.close()
+ await once(server, 'close')
+})
diff --git a/test/jest/interceptor.test.js b/test/jest/interceptor.test.js
new file mode 100644
index 0000000..73d70b7
--- /dev/null
+++ b/test/jest/interceptor.test.js
@@ -0,0 +1,197 @@
+'use strict'
+
+const { createServer } = require('http')
+const { Agent, request } = require('../../index')
+const DecoratorHandler = require('../../lib/handler/DecoratorHandler')
+/* global expect */
+
+const defaultOpts = { keepAliveTimeout: 10, keepAliveMaxTimeout: 10 }
+
+describe('interceptors', () => {
+ let server
+ beforeEach(async () => {
+ server = createServer((req, res) => {
+ res.setHeader('Content-Type', 'text/plain')
+ res.end('hello')
+ })
+ await new Promise((resolve) => { server.listen(0, resolve) })
+ })
+ afterEach(async () => {
+ await new Promise((resolve) => server.close(resolve))
+ })
+
+ test('interceptors are applied on client from an agent', async () => {
+ const interceptors = []
+ const buildInterceptor = dispatch => {
+ const interceptorContext = { requestCount: 0 }
+ interceptors.push(interceptorContext)
+ return (opts, handler) => {
+ interceptorContext.requestCount++
+ return dispatch(opts, handler)
+ }
+ }
+
+ const opts = { interceptors: { Client: [buildInterceptor] }, ...defaultOpts }
+ const agent = new Agent(opts)
+ const origin = new URL(`http://localhost:${server.address().port}`)
+ await Promise.all([
+ request(origin, { dispatcher: agent }),
+ request(origin, { dispatcher: agent })
+ ])
+
+ // Assert that the requests are run on different interceptors (different Clients)
+ const requestCounts = interceptors.map(x => x.requestCount)
+ expect(requestCounts).toEqual([1, 1])
+ })
+
+ test('interceptors are applied in the correct order', async () => {
+ const setHeaderInterceptor = (dispatch) => {
+ return (opts, handler) => {
+ opts.headers.push('foo', 'bar')
+ return dispatch(opts, handler)
+ }
+ }
+
+ const assertHeaderInterceptor = (dispatch) => {
+ return (opts, handler) => {
+ expect(opts.headers).toEqual(['foo', 'bar'])
+ return dispatch(opts, handler)
+ }
+ }
+
+ const opts = { interceptors: { Pool: [setHeaderInterceptor, assertHeaderInterceptor] }, ...defaultOpts }
+ const agent = new Agent(opts)
+ const origin = new URL(`http://localhost:${server.address().port}`)
+ await request(origin, { dispatcher: agent, headers: [] })
+ })
+
+ test('interceptors handlers are called in reverse order', async () => {
+ const clearResponseHeadersInterceptor = (dispatch) => {
+ return (opts, handler) => {
+ class ResultInterceptor extends DecoratorHandler {
+ onHeaders (statusCode, headers, resume) {
+ return super.onHeaders(statusCode, [], resume)
+ }
+ }
+
+ return dispatch(opts, new ResultInterceptor(handler))
+ }
+ }
+
+ const assertHeaderInterceptor = (dispatch) => {
+ return (opts, handler) => {
+ class ResultInterceptor extends DecoratorHandler {
+ onHeaders (statusCode, headers, resume) {
+ expect(headers).toEqual([])
+ return super.onHeaders(statusCode, headers, resume)
+ }
+ }
+
+ return dispatch(opts, new ResultInterceptor(handler))
+ }
+ }
+
+ const opts = { interceptors: { Agent: [assertHeaderInterceptor, clearResponseHeadersInterceptor] }, ...defaultOpts }
+ const agent = new Agent(opts)
+ const origin = new URL(`http://localhost:${server.address().port}`)
+ await request(origin, { dispatcher: agent, headers: [] })
+ })
+})
+
+describe('interceptors with NtlmRequestHandler', () => {
+ class FakeNtlmRequestHandler {
+ constructor (dispatch, opts, handler) {
+ this.dispatch = dispatch
+ this.opts = opts
+ this.handler = handler
+ this.requestCount = 0
+ }
+
+ onConnect (...args) {
+ return this.handler.onConnect(...args)
+ }
+
+ onError (...args) {
+ return this.handler.onError(...args)
+ }
+
+ onUpgrade (...args) {
+ return this.handler.onUpgrade(...args)
+ }
+
+ onHeaders (statusCode, headers, resume, statusText) {
+ this.requestCount++
+ if (this.requestCount < 2) {
+ // Do nothing
+ } else {
+ return this.handler.onHeaders(statusCode, headers, resume, statusText)
+ }
+ }
+
+ onData (...args) {
+ if (this.requestCount < 2) {
+ // Do nothing
+ } else {
+ return this.handler.onData(...args)
+ }
+ }
+
+ onComplete (...args) {
+ if (this.requestCount < 2) {
+ this.dispatch(this.opts, this)
+ } else {
+ return this.handler.onComplete(...args)
+ }
+ }
+
+ onBodySent (...args) {
+ if (this.requestCount < 2) {
+ // Do nothing
+ } else {
+ return this.handler.onBodySent(...args)
+ }
+ }
+ }
+ let server
+
+ beforeEach(async () => {
+ // This Test is important because NTLM and Negotiate require several
+ // http requests in sequence to run on the same keepAlive socket
+
+ const socketRequestCountSymbol = Symbol('Socket Request Count')
+ server = createServer((req, res) => {
+ if (req.socket[socketRequestCountSymbol] === undefined) {
+ req.socket[socketRequestCountSymbol] = 0
+ }
+ req.socket[socketRequestCountSymbol]++
+ res.setHeader('Content-Type', 'text/plain')
+
+ // Simulate NTLM/Negotiate logic, by returning 200
+ // on the second request of each socket
+ if (req.socket[socketRequestCountSymbol] >= 2) {
+ res.statusCode = 200
+ res.end()
+ } else {
+ res.statusCode = 401
+ res.end()
+ }
+ })
+ await new Promise((resolve) => { server.listen(0, resolve) })
+ })
+ afterEach(async () => {
+ await new Promise((resolve) => server.close(resolve))
+ })
+
+ test('Retry interceptor on Client will use the same socket', async () => {
+ const interceptor = dispatch => {
+ return (opts, handler) => {
+ return dispatch(opts, new FakeNtlmRequestHandler(dispatch, opts, handler))
+ }
+ }
+ const opts = { interceptors: { Client: [interceptor] }, ...defaultOpts }
+ const agent = new Agent(opts)
+ const origin = new URL(`http://localhost:${server.address().port}`)
+ const { statusCode } = await request(origin, { dispatcher: agent, headers: [] })
+ expect(statusCode).toEqual(200)
+ })
+})
diff --git a/test/jest/issue-1757.test.js b/test/jest/issue-1757.test.js
new file mode 100644
index 0000000..b6519d9
--- /dev/null
+++ b/test/jest/issue-1757.test.js
@@ -0,0 +1,61 @@
+'use strict'
+
+const { Dispatcher, setGlobalDispatcher, MockAgent } = require('../..')
+
+/* global expect, it */
+
+class MiniflareDispatcher extends Dispatcher {
+ constructor (inner, options) {
+ super(options)
+ this.inner = inner
+ }
+
+ dispatch (options, handler) {
+ return this.inner.dispatch(options, handler)
+ }
+
+ close (...args) {
+ return this.inner.close(...args)
+ }
+
+ destroy (...args) {
+ return this.inner.destroy(...args)
+ }
+}
+
+const runIf = (condition) => condition ? it : it.skip
+const nodeMajor = Number(process.versions.node.split('.', 1)[0])
+
+runIf(nodeMajor >= 16)('https://github.com/nodejs/undici/issues/1757', async () => {
+ // fetch isn't exported in <16.8
+ const { fetch } = require('../..')
+
+ const mockAgent = new MockAgent()
+ const mockClient = mockAgent.get('http://localhost:3000')
+ mockAgent.disableNetConnect()
+ setGlobalDispatcher(new MiniflareDispatcher(mockAgent))
+
+ mockClient.intercept({
+ path: () => true,
+ method: () => true
+ }).reply(200, async (opts) => {
+ if (opts.body?.[Symbol.asyncIterator]) {
+ const chunks = []
+ for await (const chunk of opts.body) {
+ chunks.push(chunk)
+ }
+
+ return Buffer.concat(chunks)
+ }
+
+ return opts.body
+ })
+
+ const response = await fetch('http://localhost:3000', {
+ method: 'POST',
+ body: JSON.stringify({ foo: 'bar' })
+ })
+
+ expect(response.json()).resolves.toMatchObject({ foo: 'bar' })
+ expect(response.status).toBe(200)
+})
diff --git a/test/jest/mock-agent.test.js b/test/jest/mock-agent.test.js
new file mode 100644
index 0000000..6f6bac2
--- /dev/null
+++ b/test/jest/mock-agent.test.js
@@ -0,0 +1,46 @@
+'use strict'
+
+const { request, setGlobalDispatcher, MockAgent } = require('../..')
+const { getResponse } = require('../../lib/mock/mock-utils')
+
+/* global describe, it, expect */
+
+describe('MockAgent', () => {
+ let mockAgent
+
+ afterEach(() => {
+ mockAgent.close()
+ })
+
+ it('should work in jest', async () => {
+ expect.assertions(4)
+
+ const baseUrl = 'http://localhost:9999'
+
+ mockAgent = new MockAgent()
+ setGlobalDispatcher(mockAgent)
+ const mockClient = mockAgent.get(baseUrl)
+
+ mockClient.intercept({
+ path: '/foo?hello=there&see=ya',
+ method: 'POST',
+ body: 'form1=data1&form2=data2'
+ }).reply(200, { foo: 'bar' }, {
+ headers: {
+ 'content-type': 'application/json'
+ },
+ trailers: { 'Content-MD5': 'test' }
+ })
+
+ const { statusCode, headers, trailers, body } = await request(`${baseUrl}/foo?hello=there&see=ya`, {
+ method: 'POST',
+ body: 'form1=data1&form2=data2'
+ })
+ expect(statusCode).toBe(200)
+ expect(headers).toEqual({ 'content-type': 'application/json' })
+ expect(trailers).toEqual({ 'content-md5': 'test' })
+
+ const jsonResponse = JSON.parse(await getResponse(body))
+ expect(jsonResponse).toEqual({ foo: 'bar' })
+ })
+})
diff --git a/test/jest/mock-scope.test.js b/test/jest/mock-scope.test.js
new file mode 100644
index 0000000..cab77f6
--- /dev/null
+++ b/test/jest/mock-scope.test.js
@@ -0,0 +1,32 @@
+const { MockAgent, setGlobalDispatcher, request } = require('../../index')
+
+/* global afterAll, expect, it, AbortController */
+
+const runIf = (condition) => condition ? it : it.skip
+
+const nodeMajor = Number(process.versions.node.split('.', 1)[0])
+const mockAgent = new MockAgent()
+
+afterAll(async () => {
+ await mockAgent.close()
+})
+
+runIf(nodeMajor >= 16)('Jest works with MockScope.delay - issue #1327', async () => {
+ mockAgent.disableNetConnect()
+ setGlobalDispatcher(mockAgent)
+
+ const mockPool = mockAgent.get('http://localhost:3333')
+
+ mockPool.intercept({
+ path: '/jest-bugs',
+ method: 'GET'
+ }).reply(200, 'Hello').delay(100)
+
+ const ac = new AbortController()
+ setTimeout(() => ac.abort(), 5)
+ const promise = request('http://localhost:3333/jest-bugs', {
+ signal: ac.signal
+ })
+
+ await expect(promise).rejects.toThrowError('Request aborted')
+}, 1000)
diff --git a/test/jest/test.js b/test/jest/test.js
new file mode 100644
index 0000000..079a41f
--- /dev/null
+++ b/test/jest/test.js
@@ -0,0 +1,36 @@
+'use strict'
+
+const { Client } = require('../..')
+const { createServer } = require('http')
+/* global test, expect */
+
+test('should work in jest', async () => {
+ const server = createServer((req, res) => {
+ expect(req.url).toBe('/')
+ expect(req.method).toBe('POST')
+ expect(req.headers.host).toBe(`localhost:${server.address().port}`)
+ res.setHeader('Content-Type', 'text/plain')
+ res.end('hello')
+ })
+ await expect(new Promise((resolve, reject) => {
+ server.listen(0, () => {
+ const client = new Client(`http://localhost:${server.address().port}`)
+ client.request({
+ path: '/',
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: '{}'
+ }, (err, result) => {
+ server.close()
+ client.close()
+ if (err) {
+ reject(err)
+ } else {
+ resolve(result.body.text())
+ }
+ })
+ })
+ })).resolves.toBe('hello')
+})