diff --git a/docs/api/Dispatcher.md b/docs/api/Dispatcher.md index 0c678fc8623..8252294ce29 100644 --- a/docs/api/Dispatcher.md +++ b/docs/api/Dispatcher.md @@ -209,11 +209,9 @@ Returns: `Boolean` - `false` if dispatcher is busy and further dispatch calls wo * **onConnect** `(abort: () => void, context: object) => void` - Invoked before request is dispatched on socket. May be invoked multiple times when a request is retried when the request at the head of the pipeline fails. * **onError** `(error: Error) => void` - Invoked when an error has occurred. May not throw. * **onUpgrade** `(statusCode: number, headers: Buffer[], socket: Duplex) => void` (optional) - Invoked when request is upgraded. Required if `DispatchOptions.upgrade` is defined or `DispatchOptions.method === 'CONNECT'`. -* **onResponseStarted** `() => void` (optional) - Invoked when response is received, before headers have been read. * **onHeaders** `(statusCode: number, headers: Buffer[], resume: () => void, statusText: string) => boolean` - Invoked when statusCode and headers have been received. May be invoked multiple times due to 1xx informational headers. Not required for `upgrade` requests. * **onData** `(chunk: Buffer) => boolean` - Invoked when response payload data is received. Not required for `upgrade` requests. * **onComplete** `(trailers: Buffer[]) => void` - Invoked when response payload and trailers have been received and the request has completed. Not required for `upgrade` requests. -* **onBodySent** `(chunk: string | Buffer | Uint8Array) => void` - Invoked when a body chunk is sent to the server. Not required. For a stream or iterable body this will be invoked for every chunk. For other body types, it will be invoked once after the body is sent. #### Example 1 - Dispatch GET request diff --git a/docs/api/RedirectHandler.md b/docs/api/RedirectHandler.md index 90a937e7c13..25aaa290414 100644 --- a/docs/api/RedirectHandler.md +++ b/docs/api/RedirectHandler.md @@ -86,11 +86,3 @@ Called when the request is complete. Parameters: - **trailers** `object` - The trailers received. - -#### `onBodySent(chunk)` - -Called when the request body is sent. - -Parameters: - -- **chunk** `Buffer` - The chunk of the request body sent. diff --git a/docs/api/RetryHandler.md b/docs/api/RetryHandler.md index 2323ce47911..9b5437db958 100644 --- a/docs/api/RetryHandler.md +++ b/docs/api/RetryHandler.md @@ -73,7 +73,6 @@ const handler = new RetryHandler( }, handler: { onConnect() {}, - onBodySent() {}, onHeaders(status, _rawHeaders, resume, _statusMessage) { // do something with headers }, @@ -98,7 +97,6 @@ const handler = new RetryHandler(dispatchOptions, { dispatch: client.dispatch.bind(client), handler: { onConnect() {}, - onBodySent() {}, onHeaders(status, _rawHeaders, resume, _statusMessage) {}, onData(chunk) {}, onComplete() {}, diff --git a/lib/client.js b/lib/client.js index 98adffdf182..f1e16b2e01f 100644 --- a/lib/client.js +++ b/lib/client.js @@ -726,7 +726,6 @@ class Parser { if (!request) { return -1 } - request.onResponseStarted() } onHeaderField (buf) { @@ -1606,7 +1605,6 @@ function write (client, request) { assert(contentLength === null, 'no body must not have content length') socket.write(`${header}\r\n`, 'latin1') } - request.onRequestSent() } else if (util.isBuffer(body)) { assert(contentLength === body.byteLength, 'buffer body must have content length') @@ -1614,8 +1612,6 @@ function write (client, request) { socket.write(`${header}content-length: ${contentLength}\r\n\r\n`, 'latin1') socket.write(body) socket.uncork() - request.onBodySent(body) - request.onRequestSent() if (!expectsPayload) { socket[kReset] = true } @@ -1789,7 +1785,6 @@ function writeH2 (client, session, request) { stream.once('response', headers => { const { [HTTP2_HEADER_STATUS]: statusCode, ...realHeaders } = headers - request.onResponseStarted() if (request.onHeaders(Number(statusCode), realHeaders, stream.resume.bind(stream), '') === false) { stream.pause() @@ -1870,15 +1865,13 @@ function writeH2 (client, session, request) { function writeBodyH2 () { /* istanbul ignore else: assertion */ if (!body) { - request.onRequestSent() + // Do nothing... } else if (util.isBuffer(body)) { assert(contentLength === body.byteLength, 'buffer body must have content length') stream.cork() stream.write(body) stream.uncork() stream.end() - request.onBodySent(body) - request.onRequestSent() } else if (util.isBlobLike(body)) { if (typeof body.stream === 'function') { writeIterable({ @@ -1943,22 +1936,14 @@ function writeStream ({ h2stream, body, client, request, socket, contentLength, if (err) { util.destroy(body, err) util.destroy(h2stream, err) - } else { - request.onRequestSent() } } ) - pipe.on('data', onPipeData) pipe.once('end', () => { - pipe.removeListener('data', onPipeData) util.destroy(pipe) }) - function onPipeData (chunk) { - request.onBodySent(chunk) - } - return } @@ -2074,9 +2059,6 @@ async function writeBlob ({ h2stream, body, client, request, socket, contentLeng socket.uncork() } - request.onBodySent(buffer) - request.onRequestSent() - if (!expectsPayload) { socket[kReset] = true } @@ -2122,7 +2104,6 @@ async function writeIterable ({ h2stream, body, client, request, socket, content } const res = h2stream.write(chunk) - request.onBodySent(chunk) if (!res) { await waitForDrain() } @@ -2130,7 +2111,6 @@ async function writeIterable ({ h2stream, body, client, request, socket, content } catch (err) { h2stream.destroy(err) } finally { - request.onRequestSent() h2stream.end() h2stream .off('close', onDrain) @@ -2181,7 +2161,7 @@ class AsyncWriter { } write (chunk) { - const { socket, request, contentLength, client, bytesWritten, expectsPayload, header } = this + const { socket, contentLength, client, bytesWritten, expectsPayload, header } = this if (socket[kError]) { throw socket[kError] @@ -2229,8 +2209,6 @@ class AsyncWriter { socket.uncork() - request.onBodySent(chunk) - if (!ret) { if (socket[kParser].timeout && socket[kParser].timeoutType === TIMEOUT_HEADERS) { // istanbul ignore else: only for jest @@ -2244,8 +2222,7 @@ class AsyncWriter { } end () { - const { socket, contentLength, client, bytesWritten, expectsPayload, header, request } = this - request.onRequestSent() + const { socket, contentLength, client, bytesWritten, expectsPayload, header } = this socket[kWriting] = false diff --git a/lib/core/request.js b/lib/core/request.js index 74e0ca16eaa..8577a96d604 100644 --- a/lib/core/request.js +++ b/lib/core/request.js @@ -201,30 +201,6 @@ class Request { } } - onBodySent (chunk) { - if (this[kHandler].onBodySent) { - try { - return this[kHandler].onBodySent(chunk) - } catch (err) { - this.abort(err) - } - } - } - - onRequestSent () { - if (channels.bodySent.hasSubscribers) { - channels.bodySent.publish({ request: this }) - } - - if (this[kHandler].onRequestSent) { - try { - return this[kHandler].onRequestSent() - } catch (err) { - this.abort(err) - } - } - } - onConnect (abort) { assert(!this.aborted) assert(!this.completed) @@ -237,10 +213,6 @@ class Request { } } - onResponseStarted () { - return this[kHandler].onResponseStarted?.() - } - onHeaders (statusCode, headers, resume, statusText) { assert(!this.aborted) assert(!this.completed) diff --git a/lib/core/util.js b/lib/core/util.js index 55bf9f49822..d4ef4b33430 100644 --- a/lib/core/util.js +++ b/lib/core/util.js @@ -323,10 +323,6 @@ function validateHandler (handler, method, upgrade) { throw new InvalidArgumentError('invalid onError method') } - if (typeof handler.onBodySent !== 'function' && handler.onBodySent !== undefined) { - throw new InvalidArgumentError('invalid onBodySent method') - } - if (upgrade || method === 'CONNECT') { if (typeof handler.onUpgrade !== 'function') { throw new InvalidArgumentError('invalid onUpgrade method') diff --git a/lib/fetch/index.js b/lib/fetch/index.js index 4b89b4a48e3..ef6b5acb2c1 100644 --- a/lib/fetch/index.js +++ b/lib/fetch/index.js @@ -2113,15 +2113,13 @@ async function httpNetworkFetch ( timingInfo.finalNetworkRequestStartTime = coarsenedSharedCurrentTime(fetchParams.crossOriginIsolatedCapability) }, - onResponseStarted () { + onHeaders (status, rawHeaders, resume, statusText) { // Set timingInfo’s final network-response start time to the coarsened shared current // time given fetchParams’s cross-origin isolated capability, immediately after the // user agent’s HTTP parser receives the first byte of the response (e.g., frame header // bytes for HTTP/2 or response status line for HTTP/1.x). timingInfo.finalNetworkResponseStartTime = coarsenedSharedCurrentTime(fetchParams.crossOriginIsolatedCapability) - }, - onHeaders (status, rawHeaders, resume, statusText) { if (status < 200) { return } diff --git a/lib/handler/DecoratorHandler.js b/lib/handler/DecoratorHandler.js index 9d70a767f1e..cbf0ddd4638 100644 --- a/lib/handler/DecoratorHandler.js +++ b/lib/handler/DecoratorHandler.js @@ -28,8 +28,4 @@ module.exports = class DecoratorHandler { onComplete (...args) { return this.handler.onComplete(...args) } - - onBodySent (...args) { - return this.handler.onBodySent(...args) - } } diff --git a/lib/handler/RedirectHandler.js b/lib/handler/RedirectHandler.js index 368ef520d76..2afbf9427ff 100644 --- a/lib/handler/RedirectHandler.js +++ b/lib/handler/RedirectHandler.js @@ -173,12 +173,6 @@ class RedirectHandler { this.handler.onComplete(trailers) } } - - onBodySent (chunk) { - if (this.handler.onBodySent) { - this.handler.onBodySent(chunk) - } - } } function parseLocation (statusCode, headers) { diff --git a/lib/handler/RetryHandler.js b/lib/handler/RetryHandler.js index 2a2f878c00f..5cf36c772af 100644 --- a/lib/handler/RetryHandler.js +++ b/lib/handler/RetryHandler.js @@ -74,12 +74,6 @@ class RetryHandler { }) } - onRequestSent () { - if (this.handler.onRequestSent) { - this.handler.onRequestSent() - } - } - onUpgrade (statusCode, headers, socket) { if (this.handler.onUpgrade) { this.handler.onUpgrade(statusCode, headers, socket) @@ -94,10 +88,6 @@ class RetryHandler { } } - onBodySent (chunk) { - if (this.handler.onBodySent) return this.handler.onBodySent(chunk) - } - static [kRetryHandlerDefaultRetry] (err, { state, opts }, cb) { const { statusCode, code, headers } = err const { method, retryOptions } = opts diff --git a/package.json b/package.json index a52222ce200..1ce1a058b1b 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "lint:fix": "standard --fix | snazzy", "test": "node scripts/generate-pem && npm run test:tap && npm run test:node-fetch && npm run test:fetch && npm run test:cookies && npm run test:eventsource && npm run test:wpt && npm run test:websocket && npm run test:jest && npm run test:typescript && npm run test:node-test", "test:cookies": "borp --coverage -p \"test/cookie/*.js\"", - "test:node-fetch": "mocha --exit test/node-fetch", + "test:node-fetch": "borp --coverage -p \"test/node-fetch/**/*.js\"", "test:eventsource": "npm run build:node && borp --expose-gc --coverage -p \"test/eventsource/*.js\"", "test:fetch": "npm run build:node && borp --expose-gc --coverage -p \"test/fetch/*.js\" && borp --coverage -p \"test/webidl/*.js\"", "test:jest": "jest", @@ -104,17 +104,15 @@ "@sinonjs/fake-timers": "^11.1.0", "@types/node": "^18.0.3", "abort-controller": "^3.0.0", + "axios": "^1.6.5", "borp": "^0.9.1", - "chai": "^4.3.4", - "chai-as-promised": "^7.1.1", - "chai-iterator": "^3.0.2", - "chai-string": "^1.5.0", "concurrently": "^8.0.1", "cronometro": "^2.0.2", "dns-packet": "^5.4.0", "docsify-cli": "^4.4.3", "form-data": "^4.0.0", "formdata-node": "^6.0.3", + "got": "^14.0.0", "https-pem": "^3.0.0", "husky": "^9.0.7", "import-fresh": "^3.3.0", @@ -122,11 +120,11 @@ "jsdom": "^24.0.0", "jsfuzz": "^1.0.15", "mitata": "^0.1.8", - "mocha": "^10.0.0", - "p-timeout": "^3.2.0", + "node-fetch": "^3.3.2", "pre-commit": "^1.2.2", "proxy": "^1.0.2", "proxyquire": "^2.1.3", + "request": "^2.88.2", "sinon": "^17.0.1", "snazzy": "^9.0.0", "standard": "^17.0.0", @@ -134,18 +132,14 @@ "tsd": "^0.30.1", "typescript": "^5.0.2", "wait-on": "^7.0.1", - "ws": "^8.11.0", - "axios": "^1.6.5", - "got": "^14.0.0", - "node-fetch": "^3.3.2", - "request": "^2.88.2" + "ws": "^8.11.0" }, "engines": { "node": ">=18.0" }, "standard": { "env": [ - "mocha" + "jest" ], "ignore": [ "lib/llhttp/constants.js", diff --git a/test/http2.js b/test/http2.js index 2daef09bd2c..3db608f5abe 100644 --- a/test/http2.js +++ b/test/http2.js @@ -804,7 +804,7 @@ test('Should handle h2 request with body (string or buffer) - dispatch', t => { stream.end('hello h2!') }) - t.plan(7) + t.plan(6) server.listen(0, () => { const client = new Client(`https://localhost:${server.address().port}`, { @@ -842,9 +842,6 @@ test('Should handle h2 request with body (string or buffer) - dispatch', t => { onData (chunk) { response.push(chunk) }, - onBodySent (body) { - t.equal(body.toString('utf-8'), expectedBody) - }, onComplete () { t.equal(Buffer.concat(response).toString('utf-8'), 'hello h2!') t.equal( diff --git a/test/jest/interceptor.test.js b/test/jest/interceptor.test.js index 9caa0758936..7c3d62251ea 100644 --- a/test/jest/interceptor.test.js +++ b/test/jest/interceptor.test.js @@ -143,14 +143,6 @@ describe('interceptors with NtlmRequestHandler', () => { return this.handler.onComplete(...args) } } - - onBodySent (...args) { - if (this.requestCount < 2) { - // Do nothing - } else { - return this.handler.onBodySent(...args) - } - } } let server diff --git a/test/mock-agent.js b/test/mock-agent.js index 42dfb9554cc..74758d98943 100644 --- a/test/mock-agent.js +++ b/test/mock-agent.js @@ -149,7 +149,7 @@ test('MockAgent - dispatch', t => { }) t.test('should throw if handler is not valid on redirect', (t) => { - t.plan(7) + t.plan(6) const baseUrl = 'http://localhost:9999' @@ -173,16 +173,6 @@ test('MockAgent - dispatch', t => { onConnect: 'INVALID' }), new InvalidArgumentError('invalid onConnect method')) - t.throws(() => mockAgent.dispatch({ - origin: baseUrl, - path: '/foo', - method: 'GET' - }, { - onError: (err) => { throw err }, - onConnect: () => {}, - onBodySent: 'INVALID' - }), new InvalidArgumentError('invalid onBodySent method')) - t.throws(() => mockAgent.dispatch({ origin: baseUrl, path: '/foo', @@ -190,7 +180,6 @@ test('MockAgent - dispatch', t => { }, { onError: (err) => { throw err }, onConnect: () => {}, - onBodySent: () => {}, onUpgrade: 'INVALID' }), new InvalidArgumentError('invalid onUpgrade method')) @@ -201,7 +190,6 @@ test('MockAgent - dispatch', t => { }, { onError: (err) => { throw err }, onConnect: () => {}, - onBodySent: () => {}, onHeaders: 'INVALID' }), new InvalidArgumentError('invalid onHeaders method')) @@ -212,7 +200,6 @@ test('MockAgent - dispatch', t => { }, { onError: (err) => { throw err }, onConnect: () => {}, - onBodySent: () => {}, onHeaders: () => {}, onData: 'INVALID' }), new InvalidArgumentError('invalid onData method')) @@ -224,7 +211,6 @@ test('MockAgent - dispatch', t => { }, { onError: (err) => { throw err }, onConnect: () => {}, - onBodySent: () => {}, onHeaders: () => {}, onData: () => {}, onComplete: 'INVALID' @@ -797,7 +783,7 @@ test('MockAgent - handle delays to simulate work', async (t) => { const response = await getResponse(body) t.equal(response, 'hello') const elapsedInMs = process.hrtime(start)[1] / 1e6 - t.ok(elapsedInMs >= 50, `Elapsed time is not greater than 50ms: ${elapsedInMs}`) + t.ok(elapsedInMs >= 49, `Elapsed time is not greater than 50ms: ${elapsedInMs}`) }) test('MockAgent - should persist requests', async (t) => { diff --git a/test/node-fetch/headers.js b/test/node-fetch/headers.js index 2001e976efa..8500818f354 100644 --- a/test/node-fetch/headers.js +++ b/test/node-fetch/headers.js @@ -1,18 +1,14 @@ -/* eslint no-unused-expressions: "off" */ +'use strict' +const assert = require('node:assert') +const { describe, it } = require('node:test') const { format } = require('node:util') -const chai = require('chai') -const chaiIterator = require('chai-iterator') -const { Headers } = require('../../lib/fetch/headers.js') - -chai.use(chaiIterator) - -const { expect } = chai +const { Headers } = require('../../index.js') describe('Headers', () => { it('should have attributes conforming to Web IDL', () => { const headers = new Headers() - expect(Object.getOwnPropertyNames(headers)).to.be.empty + assert.strictEqual(Object.getOwnPropertyNames(headers).length, 0) const enumerableProperties = [] for (const property in headers) { @@ -30,7 +26,7 @@ describe('Headers', () => { 'set', 'values' ]) { - expect(enumerableProperties).to.contain(toCheck) + assert.strictEqual(enumerableProperties.includes(toCheck), true) } }) @@ -41,14 +37,14 @@ describe('Headers', () => { ['b', '3'], ['a', '1'] ]) - expect(headers).to.have.property('forEach') + assert.strictEqual(typeof headers.forEach, 'function') const result = [] for (const [key, value] of headers.entries()) { result.push([key, value]) } - expect(result).to.deep.equal([ + assert.deepStrictEqual(result, [ ['a', '1'], ['b', '2, 3'], ['c', '4'] @@ -66,15 +62,15 @@ describe('Headers', () => { results.push({ value, key, object }) }) - expect(results.length).to.equal(2) - expect({ key: 'accept', value: 'application/json, text/plain', object: headers }).to.deep.equal(results[0]) - expect({ key: 'content-type', value: 'text/html', object: headers }).to.deep.equal(results[1]) + assert.strictEqual(results.length, 2) + assert.deepStrictEqual(results[0], { key: 'accept', value: 'application/json, text/plain', object: headers }) + assert.deepStrictEqual(results[1], { key: 'content-type', value: 'text/html', object: headers }) }) - xit('should set "this" to undefined by default on forEach', () => { + it.skip('should set "this" to undefined by default on forEach', () => { const headers = new Headers({ Accept: 'application/json' }) headers.forEach(function () { - expect(this).to.be.undefined + assert.strictEqual(this, undefined) }) }) @@ -82,7 +78,7 @@ describe('Headers', () => { const headers = new Headers({ Accept: 'application/json' }) const thisArg = {} headers.forEach(function () { - expect(this).to.equal(thisArg) + assert.strictEqual(this, thisArg) }, thisArg) }) @@ -93,14 +89,14 @@ describe('Headers', () => { ['a', '1'] ]) headers.append('b', '3') - expect(headers).to.be.iterable + assert.strictEqual(typeof headers[Symbol.iterator], 'function') const result = [] for (const pair of headers) { result.push(pair) } - expect(result).to.deep.equal([ + assert.deepStrictEqual(result, [ ['a', '1'], ['b', '2, 3'], ['c', '4'] @@ -115,12 +111,22 @@ describe('Headers', () => { ]) headers.append('b', '3') - expect(headers.entries()).to.be.iterable - .and.to.deep.iterate.over([ - ['a', '1'], - ['b', '2, 3'], - ['c', '4'] - ]) + assert.strictEqual(typeof headers.entries, 'function') + assert.strictEqual(typeof headers.entries()[Symbol.iterator], 'function') + + const entries = headers.entries() + assert.strictEqual(typeof entries.next, 'function') + assert.deepStrictEqual(entries.next().value, ['a', '1']) + assert.strictEqual(typeof entries.next, 'function') + assert.deepStrictEqual(entries.next().value, ['b', '2, 3']) + assert.strictEqual(typeof entries.next, 'function') + assert.deepStrictEqual(entries.next().value, ['c', '4']) + + assert.deepStrictEqual([...headers.entries()], [ + ['a', '1'], + ['b', '2, 3'], + ['c', '4'] + ]) }) it('should allow iterating through all headers with keys()', () => { @@ -131,8 +137,18 @@ describe('Headers', () => { ]) headers.append('b', '3') - expect(headers.keys()).to.be.iterable - .and.to.iterate.over(['a', 'b', 'c']) + assert.strictEqual(typeof headers.keys, 'function') + assert.strictEqual(typeof headers.keys()[Symbol.iterator], 'function') + + const keys = headers.keys() + assert.strictEqual(typeof keys.next, 'function') + assert.strictEqual(keys.next().value, 'a') + assert.strictEqual(typeof keys.next, 'function') + assert.strictEqual(keys.next().value, 'b') + assert.strictEqual(typeof keys.next, 'function') + assert.strictEqual(keys.next().value, 'c') + + assert.deepStrictEqual([...headers.keys()], ['a', 'b', 'c']) }) it('should allow iterating through all headers with values()', () => { @@ -143,26 +159,37 @@ describe('Headers', () => { ]) headers.append('b', '3') - expect(headers.values()).to.be.iterable - .and.to.iterate.over(['1', '2, 3', '4']) + assert.strictEqual(typeof headers.values, 'function') + assert.strictEqual(typeof headers.values()[Symbol.iterator], 'function') + + const values = headers.values() + assert.strictEqual(typeof values.next, 'function') + assert.strictEqual(values.next().value, '1') + assert.strictEqual(typeof values.next, 'function') + assert.strictEqual(values.next().value, '2, 3') + assert.strictEqual(typeof values.next, 'function') + assert.strictEqual(values.next().value, '4') + + assert.deepStrictEqual([...headers.values()], ['1', '2, 3', '4']) }) it('should reject illegal header', () => { const headers = new Headers() - expect(() => new Headers({ 'He y': 'ok' })).to.throw(TypeError) - expect(() => new Headers({ 'Hé-y': 'ok' })).to.throw(TypeError) - expect(() => new Headers({ 'He-y': 'ăk' })).to.throw(TypeError) - expect(() => headers.append('Hé-y', 'ok')).to.throw(TypeError) - expect(() => headers.delete('Hé-y')).to.throw(TypeError) - expect(() => headers.get('Hé-y')).to.throw(TypeError) - expect(() => headers.has('Hé-y')).to.throw(TypeError) - expect(() => headers.set('Hé-y', 'ok')).to.throw(TypeError) + + assert.throws(() => new Headers({ 'He y': 'ok' }), TypeError) + assert.throws(() => new Headers({ 'Hé-y': 'ok' }), TypeError) + assert.throws(() => new Headers({ 'He-y': 'ăk' }), TypeError) + assert.throws(() => headers.append('Hé-y', 'ok'), TypeError) + assert.throws(() => headers.delete('Hé-y'), TypeError) + assert.throws(() => headers.get('Hé-y'), TypeError) + assert.throws(() => headers.has('Hé-y'), TypeError) + assert.throws(() => headers.set('Hé-y', 'ok'), TypeError) // Should reject empty header - expect(() => headers.append('', 'ok')).to.throw(TypeError) + assert.throws(() => headers.append('', 'ok'), TypeError) }) - xit('should ignore unsupported attributes while reading headers', () => { - const FakeHeader = function () {} + it.skip('should ignore unsupported attributes while reading headers', () => { + const FakeHeader = function () { } // Prototypes are currently ignored // This might change in the future: #181 FakeHeader.prototype.z = 'fake' @@ -188,26 +215,26 @@ describe('Headers', () => { const h1Raw = h1.raw() - expect(h1Raw.a).to.include('string') - expect(h1Raw.b).to.include('1,2') - expect(h1Raw.c).to.include('') - expect(h1Raw.d).to.include('') - expect(h1Raw.e).to.include('1') - expect(h1Raw.f).to.include('1,2') - expect(h1Raw.g).to.include('[object Object]') - expect(h1Raw.h).to.include('undefined') - expect(h1Raw.i).to.include('null') - expect(h1Raw.j).to.include('NaN') - expect(h1Raw.k).to.include('true') - expect(h1Raw.l).to.include('false') - expect(h1Raw.m).to.include('test') - expect(h1Raw.n).to.include('1,2') - expect(h1Raw.n).to.include('3,4') - - expect(h1Raw.z).to.be.undefined + assert.strictEqual(h1Raw.a.includes('string'), true) + assert.strictEqual(h1Raw.b.includes('1,2'), true) + assert.strictEqual(h1Raw.c.includes(''), true) + assert.strictEqual(h1Raw.d.includes(''), true) + assert.strictEqual(h1Raw.e.includes('1'), true) + assert.strictEqual(h1Raw.f.includes('1,2'), true) + assert.strictEqual(h1Raw.g.includes('[object Object]'), true) + assert.strictEqual(h1Raw.h.includes('undefined'), true) + assert.strictEqual(h1Raw.i.includes('null'), true) + assert.strictEqual(h1Raw.j.includes('NaN'), true) + assert.strictEqual(h1Raw.k.includes('true'), true) + assert.strictEqual(h1Raw.l.includes('false'), true) + assert.strictEqual(h1Raw.m.includes('test'), true) + assert.strictEqual(h1Raw.n.includes('1,2'), true) + assert.strictEqual(h1Raw.n.includes('3,4'), true) + + assert.strictEqual(h1Raw.z, undefined) }) - xit('should wrap headers', () => { + it.skip('should wrap headers', () => { const h1 = new Headers({ a: '1' }) @@ -221,16 +248,16 @@ describe('Headers', () => { h3.append('a', '2') const h3Raw = h3.raw() - expect(h1Raw.a).to.include('1') - expect(h1Raw.a).to.not.include('2') + assert.strictEqual(h1Raw.a.includes('1'), true) + assert.strictEqual(h1Raw.a.includes('2'), false) - expect(h2Raw.a).to.include('1') - expect(h2Raw.a).to.not.include('2') - expect(h2Raw.b).to.include('1') + assert.strictEqual(h2Raw.a.includes('1'), true) + assert.strictEqual(h2Raw.a.includes('2'), false) + assert.strictEqual(h2Raw.b.includes('1'), true) - expect(h3Raw.a).to.include('1') - expect(h3Raw.a).to.include('2') - expect(h3Raw.b).to.include('1') + assert.strictEqual(h3Raw.a.includes('1'), true) + assert.strictEqual(h3Raw.a.includes('2'), true) + assert.strictEqual(h3Raw.b.includes('1'), true) }) it('should accept headers as an iterable of tuples', () => { @@ -241,33 +268,33 @@ describe('Headers', () => { ['b', '2'], ['a', '3'] ]) - expect(headers.get('a')).to.equal('1, 3') - expect(headers.get('b')).to.equal('2') + assert.strictEqual(headers.get('a'), '1, 3') + assert.strictEqual(headers.get('b'), '2') headers = new Headers([ new Set(['a', '1']), ['b', '2'], new Map([['a', null], ['3', null]]).keys() ]) - expect(headers.get('a')).to.equal('1, 3') - expect(headers.get('b')).to.equal('2') + assert.strictEqual(headers.get('a'), '1, 3') + assert.strictEqual(headers.get('b'), '2') headers = new Headers(new Map([ ['a', '1'], ['b', '2'] ])) - expect(headers.get('a')).to.equal('1') - expect(headers.get('b')).to.equal('2') + assert.strictEqual(headers.get('a'), '1') + assert.strictEqual(headers.get('b'), '2') }) it('should throw a TypeError if non-tuple exists in a headers initializer', () => { - expect(() => new Headers([['b', '2', 'huh?']])).to.throw(TypeError) - expect(() => new Headers(['b2'])).to.throw(TypeError) - expect(() => new Headers('b2')).to.throw(TypeError) - expect(() => new Headers({ [Symbol.iterator]: 42 })).to.throw(TypeError) + assert.throws(() => new Headers([['b', '2', 'huh?']]), TypeError) + assert.throws(() => new Headers(['b2']), TypeError) + assert.throws(() => new Headers('b2'), TypeError) + assert.throws(() => new Headers({ [Symbol.iterator]: 42 }), TypeError) }) - xit('should use a custom inspect function', () => { + it.skip('should use a custom inspect function', () => { const headers = new Headers([ ['Host', 'thehost'], ['Host', 'notthehost'], @@ -277,6 +304,6 @@ describe('Headers', () => { ]) // eslint-disable-next-line quotes - expect(format(headers)).to.equal("{ a: [ '1', '3' ], b: '2', host: 'thehost' }") + assert.strictEqual(format(headers), "{ a: [ '1', '3' ], b: '2', host: 'thehost' }") }) }) diff --git a/test/node-fetch/main.js b/test/node-fetch/main.js index 7e6da3f984f..e4b13b1f482 100644 --- a/test/node-fetch/main.js +++ b/test/node-fetch/main.js @@ -1,17 +1,14 @@ -/* eslint no-unused-expressions: "off" */ -/* globals AbortController */ +'use strict' // Test tools +const assert = require('node:assert') +const { describe, it, before, beforeEach, after } = require('node:test') +const { setTimeout: delay } = require('node:timers/promises') const zlib = require('node:zlib') const stream = require('node:stream') const vm = require('node:vm') -const chai = require('chai') const crypto = require('node:crypto') -const chaiPromised = require('chai-as-promised') -const chaiIterator = require('chai-iterator') -const chaiString = require('chai-string') const { Blob } = require('node:buffer') -const { setTimeout: delay } = require('timers/promises') const { fetch, @@ -23,25 +20,14 @@ const { Agent } = require('../../index.js') const HeadersOrig = require('../../lib/fetch/headers.js').Headers -const RequestOrig = require('../../lib/fetch/request.js').Request const ResponseOrig = require('../../lib/fetch/response.js').Response +const RequestOrig = require('../../lib/fetch/request.js').Request const TestServer = require('./utils/server.js') -const chaiTimeout = require('./utils/chai-timeout.js') - -function isNodeLowerThan (version) { - return !~process.version.localeCompare(version, undefined, { numeric: true }) -} const { Uint8Array: VMUint8Array } = vm.runInNewContext('this') -chai.use(chaiPromised) -chai.use(chaiIterator) -chai.use(chaiString) -chai.use(chaiTimeout) -const { expect } = chai - describe('node-fetch', () => { const local = new TestServer() let base @@ -63,87 +49,86 @@ describe('node-fetch', () => { it('should return a promise', () => { const url = `${base}hello` const p = fetch(url) - expect(p).to.be.an.instanceof(Promise) - expect(p).to.have.property('then') + assert.ok(p instanceof Promise) + assert.strictEqual(typeof p.then, 'function') }) it('should expose Headers, Response and Request constructors', () => { - expect(Headers).to.equal(HeadersOrig) - expect(Response).to.equal(ResponseOrig) - expect(Request).to.equal(RequestOrig) + assert.strictEqual(Headers, HeadersOrig) + assert.strictEqual(Response, ResponseOrig) + assert.strictEqual(Request, RequestOrig) }) it('should support proper toString output for Headers, Response and Request objects', () => { - expect(new Headers().toString()).to.equal('[object Headers]') - expect(new Response().toString()).to.equal('[object Response]') - expect(new Request(base).toString()).to.equal('[object Request]') + assert.strictEqual(new Headers().toString(), '[object Headers]') + assert.strictEqual(new Response().toString(), '[object Response]') + assert.strictEqual(new Request(base).toString(), '[object Request]') }) - + // TODO Should we reflect the input? it('should reject with error if url is protocol relative', () => { const url = '//example.com/' - return expect(fetch(url)).to.eventually.be.rejectedWith(TypeError) + return assert.rejects(fetch(url), new TypeError('Failed to parse URL from //example.com/')) }) it('should reject with error if url is relative path', () => { const url = '/some/path' - return expect(fetch(url)).to.eventually.be.rejectedWith(TypeError) + return assert.rejects(fetch(url), new TypeError('Failed to parse URL from /some/path')) }) + // TODO: This seems odd it('should reject with error if protocol is unsupported', () => { const url = 'ftp://example.com/' - return expect(fetch(url)).to.eventually.be.rejectedWith(TypeError) + return assert.rejects(fetch(url), new TypeError('fetch failed')) }) - it('should reject with error on network failure', function () { - this.timeout(5000) + it('should reject with error on network failure', { timeout: 5000 }, function () { const url = 'http://localhost:50000/' - return expect(fetch(url)).to.eventually.be.rejected - .and.be.an.instanceOf(TypeError) + return assert.rejects(fetch(url), new TypeError('fetch failed')) }) it('should resolve into response', () => { const url = `${base}hello` return fetch(url).then(res => { - expect(res).to.be.an.instanceof(Response) - expect(res.headers).to.be.an.instanceof(Headers) - expect(res.body).to.be.an.instanceof(ReadableStream) - expect(res.bodyUsed).to.be.false + assert.ok(res instanceof Response) + assert.ok(res.headers instanceof Headers) + assert.ok(res.body instanceof ReadableStream) + assert.strictEqual(res.bodyUsed, false) - expect(res.url).to.equal(url) - expect(res.ok).to.be.true - expect(res.status).to.equal(200) - expect(res.statusText).to.equal('OK') + assert.strictEqual(res.url, url) + assert.strictEqual(res.ok, true) + assert.strictEqual(res.status, 200) + assert.strictEqual(res.statusText, 'OK') }) }) it('Response.redirect should resolve into response', () => { const res = Response.redirect('http://localhost') - expect(res).to.be.an.instanceof(Response) - expect(res.headers).to.be.an.instanceof(Headers) - expect(res.headers.get('location')).to.equal('http://localhost/') - expect(res.status).to.equal(302) + assert.ok(res instanceof Response) + assert.ok(res.headers instanceof Headers) + assert.strictEqual(res.headers.get('location'), 'http://localhost/') + assert.strictEqual(res.status, 302) }) it('Response.redirect /w invalid url should fail', () => { - expect(() => { + assert.throws(() => { Response.redirect('localhost') - }).to.throw() + }) }) it('Response.redirect /w invalid status should fail', () => { - expect(() => { + assert.throws(() => { Response.redirect('http://localhost', 200) - }).to.throw() + }) }) it('should accept plain text response', () => { const url = `${base}plain` return fetch(url).then(res => { - expect(res.headers.get('content-type')).to.equal('text/plain') + assert.strictEqual(res.headers.get('content-type'), 'text/plain') return res.text().then(result => { - expect(res.bodyUsed).to.be.true - expect(result).to.be.a('string') - expect(result).to.equal('text') + assert.strictEqual(res.bodyUsed, true) + assert.strictEqual(typeof result, 'string') + assert.strictEqual(result, 'text') }) }) }) @@ -151,11 +136,11 @@ describe('node-fetch', () => { it('should accept html response (like plain text)', () => { const url = `${base}html` return fetch(url).then(res => { - expect(res.headers.get('content-type')).to.equal('text/html') + assert.strictEqual(res.headers.get('content-type'), 'text/html') return res.text().then(result => { - expect(res.bodyUsed).to.be.true - expect(result).to.be.a('string') - expect(result).to.equal('') + assert.strictEqual(res.bodyUsed, true) + assert.strictEqual(typeof result, 'string') + assert.strictEqual(result, '') }) }) }) @@ -163,11 +148,11 @@ describe('node-fetch', () => { it('should accept json response', () => { const url = `${base}json` return fetch(url).then(res => { - expect(res.headers.get('content-type')).to.equal('application/json') + assert.strictEqual(res.headers.get('content-type'), 'application/json') return res.json().then(result => { - expect(res.bodyUsed).to.be.true - expect(result).to.be.an('object') - expect(result).to.deep.equal({ name: 'value' }) + assert.strictEqual(res.bodyUsed, true) + assert.strictEqual(typeof result, 'object') + assert.deepStrictEqual(result, { name: 'value' }) }) }) }) @@ -180,7 +165,7 @@ describe('node-fetch', () => { return fetch(url, options).then(res => { return res.json() }).then(res => { - expect(res.headers['x-custom-header']).to.equal('abc') + assert.strictEqual(res.headers['x-custom-header'], 'abc') }) }) @@ -192,7 +177,7 @@ describe('node-fetch', () => { return fetch(url, options).then(res => { return res.json() }).then(res => { - expect(res.headers['x-custom-header']).to.equal('abc') + assert.strictEqual(res.headers['x-custom-header'], 'abc') }) }) @@ -204,7 +189,7 @@ describe('node-fetch', () => { return fetch(url, options).then(res => { return res.json() }).then(res => { - expect(res.headers['x-custom-header']).to.equal('abc,123') + assert.strictEqual(res.headers['x-custom-header'], 'abc,123') }) }) @@ -216,56 +201,56 @@ describe('node-fetch', () => { return fetch(url, options).then(res => { return res.json() }).then(res => { - expect(res.headers['x-custom-header']).to.equal('abc') + assert.strictEqual(res.headers['x-custom-header'], 'abc') }) }) it('should follow redirect code 301', () => { const url = `${base}redirect/301` return fetch(url).then(res => { - expect(res.url).to.equal(`${base}inspect`) - expect(res.status).to.equal(200) - expect(res.ok).to.be.true + assert.strictEqual(res.url, `${base}inspect`) + assert.strictEqual(res.status, 200) + assert.strictEqual(res.ok, true) }) }) it('should follow redirect code 302', () => { const url = `${base}redirect/302` return fetch(url).then(res => { - expect(res.url).to.equal(`${base}inspect`) - expect(res.status).to.equal(200) + assert.strictEqual(res.url, `${base}inspect`) + assert.strictEqual(res.status, 200) }) }) it('should follow redirect code 303', () => { const url = `${base}redirect/303` return fetch(url).then(res => { - expect(res.url).to.equal(`${base}inspect`) - expect(res.status).to.equal(200) + assert.strictEqual(res.url, `${base}inspect`) + assert.strictEqual(res.status, 200) }) }) it('should follow redirect code 307', () => { const url = `${base}redirect/307` return fetch(url).then(res => { - expect(res.url).to.equal(`${base}inspect`) - expect(res.status).to.equal(200) + assert.strictEqual(res.url, `${base}inspect`) + assert.strictEqual(res.status, 200) }) }) it('should follow redirect code 308', () => { const url = `${base}redirect/308` return fetch(url).then(res => { - expect(res.url).to.equal(`${base}inspect`) - expect(res.status).to.equal(200) + assert.strictEqual(res.url, `${base}inspect`) + assert.strictEqual(res.status, 200) }) }) it('should follow redirect chain', () => { const url = `${base}redirect/chain` return fetch(url).then(res => { - expect(res.url).to.equal(`${base}inspect`) - expect(res.status).to.equal(200) + assert.strictEqual(res.url, `${base}inspect`) + assert.strictEqual(res.status, 200) }) }) @@ -276,11 +261,11 @@ describe('node-fetch', () => { body: 'a=1' } return fetch(url, options).then(res => { - expect(res.url).to.equal(`${base}inspect`) - expect(res.status).to.equal(200) + assert.strictEqual(res.url, `${base}inspect`) + assert.strictEqual(res.status, 200) return res.json().then(result => { - expect(result.method).to.equal('GET') - expect(result.body).to.equal('') + assert.strictEqual(result.method, 'GET') + assert.strictEqual(result.body, '') }) }) }) @@ -292,11 +277,11 @@ describe('node-fetch', () => { body: 'a=1' } return fetch(url, options).then(res => { - expect(res.url).to.equal(`${base}inspect`) - expect(res.status).to.equal(200) + assert.strictEqual(res.url, `${base}inspect`) + assert.strictEqual(res.status, 200) return res.json().then(res => { - expect(res.method).to.equal('PATCH') - expect(res.body).to.equal('a=1') + assert.strictEqual(res.method, 'PATCH') + assert.strictEqual(res.body, 'a=1') }) }) }) @@ -308,11 +293,11 @@ describe('node-fetch', () => { body: 'a=1' } return fetch(url, options).then(res => { - expect(res.url).to.equal(`${base}inspect`) - expect(res.status).to.equal(200) + assert.strictEqual(res.url, `${base}inspect`) + assert.strictEqual(res.status, 200) return res.json().then(result => { - expect(result.method).to.equal('GET') - expect(result.body).to.equal('') + assert.strictEqual(result.method, 'GET') + assert.strictEqual(result.body, '') }) }) }) @@ -324,11 +309,11 @@ describe('node-fetch', () => { body: 'a=1' } return fetch(url, options).then(res => { - expect(res.url).to.equal(`${base}inspect`) - expect(res.status).to.equal(200) + assert.strictEqual(res.url, `${base}inspect`) + assert.strictEqual(res.status, 200) return res.json().then(res => { - expect(res.method).to.equal('PATCH') - expect(res.body).to.equal('a=1') + assert.strictEqual(res.method, 'PATCH') + assert.strictEqual(res.body, 'a=1') }) }) }) @@ -340,11 +325,11 @@ describe('node-fetch', () => { body: 'a=1' } return fetch(url, options).then(res => { - expect(res.url).to.equal(`${base}inspect`) - expect(res.status).to.equal(200) + assert.strictEqual(res.url, `${base}inspect`) + assert.strictEqual(res.status, 200) return res.json().then(result => { - expect(result.method).to.equal('GET') - expect(result.body).to.equal('') + assert.strictEqual(result.method, 'GET') + assert.strictEqual(result.body, '') }) }) }) @@ -356,11 +341,11 @@ describe('node-fetch', () => { body: 'a=1' } return fetch(url, options).then(res => { - expect(res.url).to.equal(`${base}inspect`) - expect(res.status).to.equal(200) + assert.strictEqual(res.url, `${base}inspect`) + assert.strictEqual(res.status, 200) return res.json().then(result => { - expect(result.method).to.equal('PATCH') - expect(result.body).to.equal('a=1') + assert.strictEqual(result.method, 'PATCH') + assert.strictEqual(result.body, 'a=1') }) }) }) @@ -371,21 +356,19 @@ describe('node-fetch', () => { method: 'PATCH', body: stream.Readable.from('tada') } - return expect(fetch(url, options)).to.eventually.be.rejected - .and.be.an.instanceOf(TypeError) + return assert.rejects(fetch(url, options), new TypeError('RequestInit: duplex option is required when sending a body.')) }) it('should obey maximum redirect, reject case', () => { const url = `${base}redirect/chain/20` - return expect(fetch(url)).to.eventually.be.rejected - .and.be.an.instanceOf(TypeError) + return assert.rejects(fetch(url), new TypeError('fetch failed')) }) it('should obey redirect chain, resolve case', () => { const url = `${base}redirect/chain/19` return fetch(url).then(res => { - expect(res.url).to.equal(`${base}inspect`) - expect(res.status).to.equal(200) + assert.strictEqual(res.url, `${base}inspect`) + assert.strictEqual(res.status, 200) }) }) @@ -394,8 +377,7 @@ describe('node-fetch', () => { const options = { redirect: 'error' } - return expect(fetch(url, options)).to.eventually.be.rejected - .and.be.an.instanceOf(TypeError) + return assert.rejects(fetch(url, options), new TypeError('fetch failed')) }) it('should support redirect mode, manual flag when there is no redirect', () => { @@ -404,9 +386,9 @@ describe('node-fetch', () => { redirect: 'manual' } return fetch(url, options).then(res => { - expect(res.url).to.equal(url) - expect(res.status).to.equal(200) - expect(res.headers.get('location')).to.be.null + assert.strictEqual(res.url, url) + assert.strictEqual(res.status, 200) + assert.strictEqual(res.headers.get('location'), null) }) }) @@ -416,19 +398,19 @@ describe('node-fetch', () => { headers: new Headers({ 'x-custom-header': 'abc' }) } return fetch(url, options).then(res => { - expect(res.url).to.equal(`${base}inspect`) + assert.strictEqual(res.url, `${base}inspect`) return res.json() }).then(res => { - expect(res.headers['x-custom-header']).to.equal('abc') + assert.strictEqual(res.headers['x-custom-header'], 'abc') }) }) it('should treat broken redirect as ordinary response (follow)', () => { const url = `${base}redirect/no-location` return fetch(url).then(res => { - expect(res.url).to.equal(url) - expect(res.status).to.equal(301) - expect(res.headers.get('location')).to.be.null + assert.strictEqual(res.url, url) + assert.strictEqual(res.status, 301) + assert.strictEqual(res.headers.get('location'), null) }) }) @@ -438,9 +420,9 @@ describe('node-fetch', () => { redirect: 'manual' } return fetch(url, options).then(res => { - expect(res.url).to.equal(url) - expect(res.status).to.equal(301) - expect(res.headers.get('location')).to.be.null + assert.strictEqual(res.url, url) + assert.strictEqual(res.status, 301) + assert.strictEqual(res.headers.get('location'), null) }) }) @@ -450,37 +432,37 @@ describe('node-fetch', () => { redirect: 'foobar' } return fetch(url, options).then(() => { - expect.fail() + assert.fail() }, error => { - expect(error).to.be.an.instanceOf(TypeError) + assert.ok(error instanceof TypeError) }) }) it('should set redirected property on response when redirect', () => { const url = `${base}redirect/301` return fetch(url).then(res => { - expect(res.redirected).to.be.true + assert.strictEqual(res.redirected, true) }) }) it('should not set redirected property on response without redirect', () => { const url = `${base}hello` return fetch(url).then(res => { - expect(res.redirected).to.be.false + assert.strictEqual(res.redirected, false) }) }) it('should handle client-error response', () => { const url = `${base}error/400` return fetch(url).then(res => { - expect(res.headers.get('content-type')).to.equal('text/plain') - expect(res.status).to.equal(400) - expect(res.statusText).to.equal('Bad Request') - expect(res.ok).to.be.false + assert.strictEqual(res.headers.get('content-type'), 'text/plain') + assert.strictEqual(res.status, 400) + assert.strictEqual(res.statusText, 'Bad Request') + assert.strictEqual(res.ok, false) return res.text().then(result => { - expect(res.bodyUsed).to.be.true - expect(result).to.be.a('string') - expect(result).to.equal('client error') + assert.strictEqual(res.bodyUsed, true) + assert.strictEqual(typeof result, 'string') + assert.strictEqual(result, 'client error') }) }) }) @@ -488,37 +470,37 @@ describe('node-fetch', () => { it('should handle server-error response', () => { const url = `${base}error/500` return fetch(url).then(res => { - expect(res.headers.get('content-type')).to.equal('text/plain') - expect(res.status).to.equal(500) - expect(res.statusText).to.equal('Internal Server Error') - expect(res.ok).to.be.false + assert.strictEqual(res.headers.get('content-type'), 'text/plain') + assert.strictEqual(res.status, 500) + assert.strictEqual(res.statusText, 'Internal Server Error') + assert.strictEqual(res.ok, false) return res.text().then(result => { - expect(res.bodyUsed).to.be.true - expect(result).to.be.a('string') - expect(result).to.equal('server error') + assert.strictEqual(res.bodyUsed, true) + assert.strictEqual(typeof result, 'string') + assert.strictEqual(result, 'server error') }) }) }) it('should handle network-error response', () => { const url = `${base}error/reset` - return expect(fetch(url)).to.eventually.be.rejectedWith(TypeError) + return assert.rejects(fetch(url), new TypeError('fetch failed')) }) it('should handle network-error partial response', () => { const url = `${base}error/premature` return fetch(url).then(res => { - expect(res.status).to.equal(200) - expect(res.ok).to.be.true - return expect(res.text()).to.eventually.be.rejectedWith(Error) + assert.strictEqual(res.status, 200) + assert.strictEqual(res.ok, true) + return assert.rejects(() => res.text(), new TypeError('terminated')) }) }) it('should handle network-error in chunked response async iterator', () => { const url = `${base}error/premature/chunked` return fetch(url).then(res => { - expect(res.status).to.equal(200) - expect(res.ok).to.be.true + assert.strictEqual(res.status, 200) + assert.strictEqual(res.ok, true) const read = async body => { const chunks = [] @@ -529,74 +511,75 @@ describe('node-fetch', () => { return chunks } - return expect(read(res.body)) - .to.eventually.be.rejectedWith(Error) + return assert.rejects(read(res.body), new TypeError('terminated')) }) }) it('should handle network-error in chunked response in consumeBody', () => { const url = `${base}error/premature/chunked` return fetch(url).then(res => { - expect(res.status).to.equal(200) - expect(res.ok).to.be.true + assert.strictEqual(res.status, 200) + assert.strictEqual(res.ok, true) - return expect(res.text()).to.eventually.be.rejectedWith(Error) + return assert.rejects(res.text(), new TypeError('terminated')) }) }) it('should handle DNS-error response', () => { const url = 'http://domain.invalid' - return expect(fetch(url)).to.eventually.be.rejectedWith(TypeError) + return assert.rejects(fetch(url), new TypeError('fetch failed')) }) + // TODO: Should we pass through the error message? it('should reject invalid json response', () => { const url = `${base}error/json` return fetch(url).then(res => { - expect(res.headers.get('content-type')).to.equal('application/json') - return expect(res.json()).to.eventually.be.rejectedWith(Error) + assert.strictEqual(res.headers.get('content-type'), 'application/json') + return assert.rejects(res.json(), SyntaxError) }) }) it('should handle response with no status text', () => { const url = `${base}no-status-text` return fetch(url).then(res => { - expect(res.statusText).to.equal('') + assert.strictEqual(res.statusText, '') }) }) it('should handle no content response', () => { const url = `${base}no-content` return fetch(url).then(res => { - expect(res.status).to.equal(204) - expect(res.statusText).to.equal('No Content') - expect(res.ok).to.be.true + assert.strictEqual(res.status, 204) + assert.strictEqual(res.statusText, 'No Content') + assert.strictEqual(res.ok, true) return res.text().then(result => { - expect(result).to.be.a('string') - expect(result).to.be.empty + assert.strictEqual(typeof result, 'string') + assert.strictEqual(result, '') }) }) }) + // TODO: Should we pass through the error message? it('should reject when trying to parse no content response as json', () => { const url = `${base}no-content` return fetch(url).then(res => { - expect(res.status).to.equal(204) - expect(res.statusText).to.equal('No Content') - expect(res.ok).to.be.true - return expect(res.json()).to.eventually.be.rejectedWith(Error) + assert.strictEqual(res.status, 204) + assert.strictEqual(res.statusText, 'No Content') + assert.strictEqual(res.ok, true) + return assert.rejects(res.json(), new SyntaxError('Unexpected end of JSON input')) }) }) it('should handle no content response with gzip encoding', () => { const url = `${base}no-content/gzip` return fetch(url).then(res => { - expect(res.status).to.equal(204) - expect(res.statusText).to.equal('No Content') - expect(res.headers.get('content-encoding')).to.equal('gzip') - expect(res.ok).to.be.true + assert.strictEqual(res.status, 204) + assert.strictEqual(res.statusText, 'No Content') + assert.strictEqual(res.headers.get('content-encoding'), 'gzip') + assert.strictEqual(res.ok, true) return res.text().then(result => { - expect(result).to.be.a('string') - expect(result).to.be.empty + assert.strictEqual(typeof result, 'string') + assert.strictEqual(result, '') }) }) }) @@ -604,12 +587,12 @@ describe('node-fetch', () => { it('should handle not modified response', () => { const url = `${base}not-modified` return fetch(url).then(res => { - expect(res.status).to.equal(304) - expect(res.statusText).to.equal('Not Modified') - expect(res.ok).to.be.false + assert.strictEqual(res.status, 304) + assert.strictEqual(res.statusText, 'Not Modified') + assert.strictEqual(res.ok, false) return res.text().then(result => { - expect(result).to.be.a('string') - expect(result).to.be.empty + assert.strictEqual(typeof result, 'string') + assert.strictEqual(result, '') }) }) }) @@ -617,13 +600,13 @@ describe('node-fetch', () => { it('should handle not modified response with gzip encoding', () => { const url = `${base}not-modified/gzip` return fetch(url).then(res => { - expect(res.status).to.equal(304) - expect(res.statusText).to.equal('Not Modified') - expect(res.headers.get('content-encoding')).to.equal('gzip') - expect(res.ok).to.be.false + assert.strictEqual(res.status, 304) + assert.strictEqual(res.statusText, 'Not Modified') + assert.strictEqual(res.headers.get('content-encoding'), 'gzip') + assert.strictEqual(res.ok, false) return res.text().then(result => { - expect(result).to.be.a('string') - expect(result).to.be.empty + assert.strictEqual(typeof result, 'string') + assert.strictEqual(result, '') }) }) }) @@ -631,10 +614,10 @@ describe('node-fetch', () => { it('should decompress gzip response', () => { const url = `${base}gzip` return fetch(url).then(res => { - expect(res.headers.get('content-type')).to.equal('text/plain') + assert.strictEqual(res.headers.get('content-type'), 'text/plain') return res.text().then(result => { - expect(result).to.be.a('string') - expect(result).to.equal('hello world') + assert.strictEqual(typeof result, 'string') + assert.strictEqual(result, 'hello world') }) }) }) @@ -642,19 +625,19 @@ describe('node-fetch', () => { it('should decompress slightly invalid gzip response', async () => { const url = `${base}gzip-truncated` const res = await fetch(url) - expect(res.headers.get('content-type')).to.equal('text/plain') + assert.strictEqual(res.headers.get('content-type'), 'text/plain') const result = await res.text() - expect(result).to.be.a('string') - expect(result).to.equal('hello world') + assert.strictEqual(typeof result, 'string') + assert.strictEqual(result, 'hello world') }) it('should decompress deflate response', () => { const url = `${base}deflate` return fetch(url).then(res => { - expect(res.headers.get('content-type')).to.equal('text/plain') + assert.strictEqual(res.headers.get('content-type'), 'text/plain') return res.text().then(result => { - expect(result).to.be.a('string') - expect(result).to.equal('hello world') + assert.strictEqual(typeof result, 'string') + assert.strictEqual(result, 'hello world') }) }) }) @@ -662,10 +645,10 @@ describe('node-fetch', () => { it('should decompress deflate raw response from old apache server', () => { const url = `${base}deflate-raw` return fetch(url).then(res => { - expect(res.headers.get('content-type')).to.equal('text/plain') + assert.strictEqual(res.headers.get('content-type'), 'text/plain') return res.text().then(result => { - expect(result).to.be.a('string') - expect(result).to.equal('hello world') + assert.strictEqual(typeof result, 'string') + assert.strictEqual(result, 'hello world') }) }) }) @@ -677,10 +660,10 @@ describe('node-fetch', () => { const url = `${base}brotli` return fetch(url).then(res => { - expect(res.headers.get('content-type')).to.equal('text/plain') + assert.strictEqual(res.headers.get('content-type'), 'text/plain') return res.text().then(result => { - expect(result).to.be.a('string') - expect(result).to.equal('hello world') + assert.strictEqual(typeof result, 'string') + assert.strictEqual(result, 'hello world') }) }) }) @@ -692,13 +675,13 @@ describe('node-fetch', () => { const url = `${base}no-content/brotli` return fetch(url).then(res => { - expect(res.status).to.equal(204) - expect(res.statusText).to.equal('No Content') - expect(res.headers.get('content-encoding')).to.equal('br') - expect(res.ok).to.be.true + assert.strictEqual(res.status, 204) + assert.strictEqual(res.statusText, 'No Content') + assert.strictEqual(res.headers.get('content-encoding'), 'br') + assert.strictEqual(res.ok, true) return res.text().then(result => { - expect(result).to.be.a('string') - expect(result).to.be.empty + assert.strictEqual(typeof result, 'string') + assert.strictEqual(result, '') }) }) }) @@ -706,10 +689,10 @@ describe('node-fetch', () => { it('should skip decompression if unsupported', () => { const url = `${base}sdch` return fetch(url).then(res => { - expect(res.headers.get('content-type')).to.equal('text/plain') + assert.strictEqual(res.headers.get('content-type'), 'text/plain') return res.text().then(result => { - expect(result).to.be.a('string') - expect(result).to.equal('fake sdch string') + assert.strictEqual(typeof result, 'string') + assert.strictEqual(result, 'fake sdch string') }) }) }) @@ -717,10 +700,10 @@ describe('node-fetch', () => { it('should skip decompression if unsupported codings', () => { const url = `${base}multiunsupported` return fetch(url).then(res => { - expect(res.headers.get('content-type')).to.equal('text/plain') + assert.strictEqual(res.headers.get('content-type'), 'text/plain') return res.text().then(result => { - expect(result).to.be.a('string') - expect(result).to.equal('multiunsupported') + assert.strictEqual(typeof result, 'string') + assert.strictEqual(result, 'multiunsupported') }) }) }) @@ -728,10 +711,10 @@ describe('node-fetch', () => { it('should decompress multiple coding', () => { const url = `${base}multisupported` return fetch(url).then(res => { - expect(res.headers.get('content-type')).to.equal('text/plain') + assert.strictEqual(res.headers.get('content-type'), 'text/plain') return res.text().then(result => { - expect(result).to.be.a('string') - expect(result).to.equal('hello world') + assert.strictEqual(typeof result, 'string') + assert.strictEqual(result, 'hello world') }) }) }) @@ -739,8 +722,8 @@ describe('node-fetch', () => { it('should reject if response compression is invalid', () => { const url = `${base}invalid-content-encoding` return fetch(url).then(res => { - expect(res.headers.get('content-type')).to.equal('text/plain') - return expect(res.text()).to.eventually.be.rejected + assert.strictEqual(res.headers.get('content-type'), 'text/plain') + return assert.rejects(res.text(), new TypeError('terminated')) }) }) @@ -748,22 +731,22 @@ describe('node-fetch', () => { const url = `${base}invalid-content-encoding` fetch(url) .then(res => { - expect(res.status).to.equal(200) + assert.strictEqual(res.status, 200) }) - .catch(() => {}) - .then(() => { + .catch(() => { }) + .then(new Promise((resolve) => { // Wait a few ms to see if a uncaught error occurs setTimeout(() => { - done() + resolve() }, 20) - }) + })) }) it('should collect handled errors on the body stream to reject if the body is used later', () => { const url = `${base}invalid-content-encoding` return fetch(url).then(delay(20)).then(res => { - expect(res.headers.get('content-type')).to.equal('text/plain') - return expect(res.text()).to.eventually.be.rejected + assert.strictEqual(res.headers.get('content-type'), 'text/plain') + return assert.rejects(res.text(), new TypeError('terminated')) }) }) @@ -776,7 +759,7 @@ describe('node-fetch', () => { } } return fetch(url, options).then(res => res.json()).then(res => { - expect(res.headers['accept-encoding']).to.equal('gzip') + assert.strictEqual(res.headers['accept-encoding'], 'gzip') }) }) @@ -804,11 +787,15 @@ describe('node-fetch', () => { controller.abort() - return Promise.all(fetches.map(fetched => expect(fetched) - .to.eventually.be.rejected - .and.be.an.instanceOf(Error) - .and.have.property('name', 'AbortError') - )) + return Promise.all(fetches.map(async fetched => { + try { + await fetched + assert.fail('should have thrown') + } catch (error) { + assert.ok(error instanceof Error) + assert.strictEqual(error.name, 'AbortError') + } + })) }) it('should support multiple request cancellation with signal', () => { @@ -829,100 +816,118 @@ describe('node-fetch', () => { controller.abort() - return Promise.all(fetches.map(fetched => expect(fetched) - .to.eventually.be.rejected - .and.be.an.instanceOf(Error) - .and.have.property('name', 'AbortError') - )) + return Promise.all(fetches.map(async fetched => { + try { + await fetched + assert.fail('should have thrown') + } catch (error) { + assert.ok(error instanceof Error) + assert.strictEqual(error.name, 'AbortError') + } + })) }) - it('should reject immediately if signal has already been aborted', () => { + it('should reject immediately if signal has already been aborted', async () => { const url = `${base}timeout` const options = { signal: controller.signal } controller.abort() const fetched = fetch(url, options) - return expect(fetched).to.eventually.be.rejected - .and.be.an.instanceOf(Error) - .and.have.property('name', 'AbortError') + + try { + await fetched + assert.fail('should have thrown') + } catch (error) { + assert.ok(error instanceof Error) + assert.strictEqual(error.name, 'AbortError') + } }) - it('should allow redirects to be aborted', () => { + it('should allow redirects to be aborted', async () => { const request = new Request(`${base}redirect/slow`, { signal: controller.signal }) setTimeout(() => { controller.abort() }, 20) - return expect(fetch(request)).to.be.eventually.rejected - .and.be.an.instanceOf(Error) - .and.have.property('name', 'AbortError') + + try { + await fetch(request) + assert.fail('should have thrown') + } catch (error) { + assert.ok(error instanceof Error) + assert.strictEqual(error.name, 'AbortError') + } }) - it('should allow redirected response body to be aborted', () => { + it('should allow redirected response body to be aborted', async () => { const request = new Request(`${base}redirect/slow-stream`, { signal: controller.signal }) - return expect(fetch(request).then(res => { - expect(res.headers.get('content-type')).to.equal('text/plain') + const fetched = fetch(request).then(res => { + assert.strictEqual(res.headers.get('content-type'), 'text/plain') const result = res.text() controller.abort() return result - })).to.be.eventually.rejected - .and.be.an.instanceOf(Error) - .and.have.property('name', 'AbortError') + }) + + try { + await fetched + assert.fail('should have thrown') + } catch (error) { + assert.ok(error instanceof Error) + assert.strictEqual(error.name, 'AbortError') + } }) - it('should reject response body with AbortError when aborted before stream has been read completely', () => { - return expect(fetch( + it('should reject response body with AbortError when aborted before stream has been read completely', async () => { + const response = await fetch( `${base}slow`, { signal: controller.signal } - )) - .to.eventually.be.fulfilled - .then(res => { - const promise = res.text() - controller.abort() - return expect(promise) - .to.eventually.be.rejected - .and.be.an.instanceof(Error) - .and.have.property('name', 'AbortError') - }) + ) + + const promise = response.text() + controller.abort() + + try { + await promise + assert.fail('should have thrown') + } catch (error) { + assert.ok(error instanceof Error) + assert.strictEqual(error.name, 'AbortError') + } }) - it('should reject response body methods immediately with AbortError when aborted before stream is disturbed', () => { - return expect(fetch( + it('should reject response body methods immediately with AbortError when aborted before stream is disturbed', async () => { + const response = await fetch( `${base}slow`, { signal: controller.signal } - )) - .to.eventually.be.fulfilled - .then(res => { - controller.abort() - return expect(res.text()) - .to.eventually.be.rejected - .and.be.an.instanceof(Error) - .and.have.property('name', 'AbortError') - }) + ) + + controller.abort() + const promise = response.text() + try { + await promise + assert.fail('should have thrown') + } catch (error) { + assert.ok(error instanceof Error) + assert.strictEqual(error.name, 'AbortError') + } }) }) it('should throw a TypeError if a signal is not of type AbortSignal or EventTarget', () => { return Promise.all([ - expect(fetch(`${base}inspect`, { signal: {} })) - .to.be.eventually.rejected - .and.be.an.instanceof(TypeError), - expect(fetch(`${base}inspect`, { signal: '' })) - .to.be.eventually.rejected - .and.be.an.instanceof(TypeError), - expect(fetch(`${base}inspect`, { signal: Object.create(null) })) - .to.be.eventually.rejected - .and.be.an.instanceof(TypeError) + assert.rejects(fetch(`${base}inspect`, { signal: {} }), new TypeError("Failed to construct 'Request': member signal is not of type AbortSignal.")), + assert.rejects(fetch(`${base}inspect`, { signal: '' }), new TypeError("Failed to construct 'Request': member signal is not of type AbortSignal.")), + assert.rejects(fetch(`${base}inspect`, { signal: Object.create(null) }), new TypeError("Failed to construct 'Request': member signal is not of type AbortSignal.")) ]) }) it('should gracefully handle a null signal', () => { return fetch(`${base}hello`, { signal: null }).then(res => { - return expect(res.ok).to.be.true + return assert.strictEqual(res.ok, true) }) }) @@ -934,14 +939,14 @@ describe('node-fetch', () => { } } return fetch(url, options).then(res => res.json()).then(res => { - expect(res.headers['user-agent']).to.equal('faked') + assert.strictEqual(res.headers['user-agent'], 'faked') }) }) it('should set default Accept header', () => { const url = `${base}inspect` fetch(url).then(res => res.json()).then(res => { - expect(res.headers.accept).to.equal('*/*') + assert.strictEqual(res.headers.accept, '*/*') }) }) @@ -953,7 +958,7 @@ describe('node-fetch', () => { } } return fetch(url, options).then(res => res.json()).then(res => { - expect(res.headers.accept).to.equal('application/json') + assert.strictEqual(res.headers.accept, 'application/json') }) }) @@ -965,10 +970,10 @@ describe('node-fetch', () => { return fetch(url, options).then(res => { return res.json() }).then(res => { - expect(res.method).to.equal('POST') - expect(res.headers['transfer-encoding']).to.be.undefined - expect(res.headers['content-type']).to.be.undefined - expect(res.headers['content-length']).to.equal('0') + assert.strictEqual(res.method, 'POST') + assert.strictEqual(res.headers['transfer-encoding'], undefined) + assert.strictEqual(res.headers['content-type'], undefined) + assert.strictEqual(res.headers['content-length'], '0') }) }) @@ -981,11 +986,11 @@ describe('node-fetch', () => { return fetch(url, options).then(res => { return res.json() }).then(res => { - expect(res.method).to.equal('POST') - expect(res.body).to.equal('a=1') - expect(res.headers['transfer-encoding']).to.be.undefined - expect(res.headers['content-type']).to.equal('text/plain;charset=UTF-8') - expect(res.headers['content-length']).to.equal('3') + assert.strictEqual(res.method, 'POST') + assert.strictEqual(res.body, 'a=1') + assert.strictEqual(res.headers['transfer-encoding'], undefined) + assert.strictEqual(res.headers['content-type'], 'text/plain;charset=UTF-8') + assert.strictEqual(res.headers['content-length'], '3') }) }) @@ -998,11 +1003,11 @@ describe('node-fetch', () => { return fetch(url, options).then(res => { return res.json() }).then(res => { - expect(res.method).to.equal('POST') - expect(res.body).to.equal('a=1') - expect(res.headers['transfer-encoding']).to.be.undefined - expect(res.headers['content-type']).to.be.undefined - expect(res.headers['content-length']).to.equal('3') + assert.strictEqual(res.method, 'POST') + assert.strictEqual(res.body, 'a=1') + assert.strictEqual(res.headers['transfer-encoding'], undefined) + assert.strictEqual(res.headers['content-type'], undefined) + assert.strictEqual(res.headers['content-length'], '3') }) }) @@ -1014,11 +1019,11 @@ describe('node-fetch', () => { body: encoder.encode('Hello, world!\n').buffer } return fetch(url, options).then(res => res.json()).then(res => { - expect(res.method).to.equal('POST') - expect(res.body).to.equal('Hello, world!\n') - expect(res.headers['transfer-encoding']).to.be.undefined - expect(res.headers['content-type']).to.be.undefined - expect(res.headers['content-length']).to.equal('14') + assert.strictEqual(res.method, 'POST') + assert.strictEqual(res.body, 'Hello, world!\n') + assert.strictEqual(res.headers['transfer-encoding'], undefined) + assert.strictEqual(res.headers['content-type'], undefined) + assert.strictEqual(res.headers['content-length'], '14') }) }) @@ -1029,11 +1034,11 @@ describe('node-fetch', () => { body: new VMUint8Array(Buffer.from('Hello, world!\n')).buffer } return fetch(url, options).then(res => res.json()).then(res => { - expect(res.method).to.equal('POST') - expect(res.body).to.equal('Hello, world!\n') - expect(res.headers['transfer-encoding']).to.be.undefined - expect(res.headers['content-type']).to.be.undefined - expect(res.headers['content-length']).to.equal('14') + assert.strictEqual(res.method, 'POST') + assert.strictEqual(res.body, 'Hello, world!\n') + assert.strictEqual(res.headers['transfer-encoding'], undefined) + assert.strictEqual(res.headers['content-type'], undefined) + assert.strictEqual(res.headers['content-length'], '14') }) }) @@ -1045,11 +1050,11 @@ describe('node-fetch', () => { body: encoder.encode('Hello, world!\n') } return fetch(url, options).then(res => res.json()).then(res => { - expect(res.method).to.equal('POST') - expect(res.body).to.equal('Hello, world!\n') - expect(res.headers['transfer-encoding']).to.be.undefined - expect(res.headers['content-type']).to.be.undefined - expect(res.headers['content-length']).to.equal('14') + assert.strictEqual(res.method, 'POST') + assert.strictEqual(res.body, 'Hello, world!\n') + assert.strictEqual(res.headers['transfer-encoding'], undefined) + assert.strictEqual(res.headers['content-type'], undefined) + assert.strictEqual(res.headers['content-length'], '14') }) }) @@ -1061,11 +1066,11 @@ describe('node-fetch', () => { body: new BigUint64Array(encoder.encode('0123456789abcdef').buffer) } return fetch(url, options).then(res => res.json()).then(res => { - expect(res.method).to.equal('POST') - expect(res.body).to.equal('0123456789abcdef') - expect(res.headers['transfer-encoding']).to.be.undefined - expect(res.headers['content-type']).to.be.undefined - expect(res.headers['content-length']).to.equal('16') + assert.strictEqual(res.method, 'POST') + assert.strictEqual(res.body, '0123456789abcdef') + assert.strictEqual(res.headers['transfer-encoding'], undefined) + assert.strictEqual(res.headers['content-type'], undefined) + assert.strictEqual(res.headers['content-length'], '16') }) }) @@ -1077,11 +1082,11 @@ describe('node-fetch', () => { body: new DataView(encoder.encode('Hello, world!\n').buffer) } return fetch(url, options).then(res => res.json()).then(res => { - expect(res.method).to.equal('POST') - expect(res.body).to.equal('Hello, world!\n') - expect(res.headers['transfer-encoding']).to.be.undefined - expect(res.headers['content-type']).to.be.undefined - expect(res.headers['content-length']).to.equal('14') + assert.strictEqual(res.method, 'POST') + assert.strictEqual(res.body, 'Hello, world!\n') + assert.strictEqual(res.headers['transfer-encoding'], undefined) + assert.strictEqual(res.headers['content-type'], undefined) + assert.strictEqual(res.headers['content-length'], '14') }) }) @@ -1092,11 +1097,11 @@ describe('node-fetch', () => { body: new VMUint8Array(Buffer.from('Hello, world!\n')) } return fetch(url, options).then(res => res.json()).then(res => { - expect(res.method).to.equal('POST') - expect(res.body).to.equal('Hello, world!\n') - expect(res.headers['transfer-encoding']).to.be.undefined - expect(res.headers['content-type']).to.be.undefined - expect(res.headers['content-length']).to.equal('14') + assert.strictEqual(res.method, 'POST') + assert.strictEqual(res.body, 'Hello, world!\n') + assert.strictEqual(res.headers['transfer-encoding'], undefined) + assert.strictEqual(res.headers['content-type'], undefined) + assert.strictEqual(res.headers['content-length'], '14') }) }) @@ -1108,11 +1113,11 @@ describe('node-fetch', () => { body: encoder.encode('Hello, world!\n').subarray(7, 13) } return fetch(url, options).then(res => res.json()).then(res => { - expect(res.method).to.equal('POST') - expect(res.body).to.equal('world!') - expect(res.headers['transfer-encoding']).to.be.undefined - expect(res.headers['content-type']).to.be.undefined - expect(res.headers['content-length']).to.equal('6') + assert.strictEqual(res.method, 'POST') + assert.strictEqual(res.body, 'world!') + assert.strictEqual(res.headers['transfer-encoding'], undefined) + assert.strictEqual(res.headers['content-type'], undefined) + assert.strictEqual(res.headers['content-length'], '6') }) }) @@ -1125,11 +1130,11 @@ describe('node-fetch', () => { return fetch(url, options).then(res => { return res.json() }).then(res => { - expect(res.method).to.equal('POST') - expect(res.body).to.equal('a=1') - expect(res.headers['transfer-encoding']).to.be.undefined - // expect(res.headers['content-type']).to.be.undefined - expect(res.headers['content-length']).to.equal('3') + assert.strictEqual(res.method, 'POST') + assert.strictEqual(res.body, 'a=1') + assert.strictEqual(res.headers['transfer-encoding'], undefined) + // assert.strictEqual(res.headers['content-type'], undefined) + assert.strictEqual(res.headers['content-length'], '3') }) }) @@ -1144,11 +1149,11 @@ describe('node-fetch', () => { return fetch(url, options).then(res => { return res.json() }).then(res => { - expect(res.method).to.equal('POST') - expect(res.body).to.equal('a=1') - expect(res.headers['transfer-encoding']).to.be.undefined - expect(res.headers['content-type']).to.equal('text/plain;charset=utf-8') - expect(res.headers['content-length']).to.equal('3') + assert.strictEqual(res.method, 'POST') + assert.strictEqual(res.body, 'a=1') + assert.strictEqual(res.headers['transfer-encoding'], undefined) + assert.strictEqual(res.headers['content-type'], 'text/plain;charset=utf-8') + assert.strictEqual(res.headers['content-length'], '3') }) }) @@ -1162,11 +1167,11 @@ describe('node-fetch', () => { return fetch(url, options).then(res => { return res.json() }).then(res => { - expect(res.method).to.equal('POST') - expect(res.body).to.equal('a=1') - expect(res.headers['transfer-encoding']).to.equal('chunked') - expect(res.headers['content-type']).to.be.undefined - expect(res.headers['content-length']).to.be.undefined + assert.strictEqual(res.method, 'POST') + assert.strictEqual(res.body, 'a=1') + assert.strictEqual(res.headers['transfer-encoding'], 'chunked') + assert.strictEqual(res.headers['content-type'], undefined) + assert.strictEqual(res.headers['content-length'], undefined) }) }) @@ -1180,10 +1185,10 @@ describe('node-fetch', () => { return fetch(url, options).then(res => { return res.json() }).then(res => { - expect(res.method).to.equal('POST') - expect(res.body).to.equal('[object Object]') - expect(res.headers['content-type']).to.equal('text/plain;charset=UTF-8') - expect(res.headers['content-length']).to.equal('15') + assert.strictEqual(res.method, 'POST') + assert.strictEqual(res.body, '[object Object]') + assert.strictEqual(res.headers['content-type'], 'text/plain;charset=UTF-8') + assert.strictEqual(res.headers['content-length'], '15') }) }) @@ -1199,9 +1204,9 @@ describe('node-fetch', () => { return fetch(url, options).then(res => { return res.json() }).then(res => { - expect(res.method).to.equal('POST') - expect(res.headers['content-type']).to.startWith('multipart/form-data; boundary=') - expect(res.body).to.equal('a=1') + assert.strictEqual(res.method, 'POST') + assert.ok(res.headers['content-type'].startsWith('multipart/form-data; boundary=')) + assert.strictEqual(res.body, 'a=1') }) }) @@ -1209,20 +1214,20 @@ describe('node-fetch', () => { const parameters = new URLSearchParams() const res = new Response(parameters) res.headers.get('Content-Type') - expect(res.headers.get('Content-Type')).to.equal('application/x-www-form-urlencoded;charset=UTF-8') + assert.strictEqual(res.headers.get('Content-Type'), 'application/x-www-form-urlencoded;charset=UTF-8') }) it('constructing a Request with URLSearchParams as body should have a Content-Type', () => { const parameters = new URLSearchParams() const request = new Request(base, { method: 'POST', body: parameters }) - expect(request.headers.get('Content-Type')).to.equal('application/x-www-form-urlencoded;charset=UTF-8') + assert.strictEqual(request.headers.get('Content-Type'), 'application/x-www-form-urlencoded;charset=UTF-8') }) it('Reading a body with URLSearchParams should echo back the result', () => { const parameters = new URLSearchParams() parameters.append('a', '1') return new Response(parameters).text().then(text => { - expect(text).to.equal('a=1') + assert.strictEqual(text, 'a=1') }) }) @@ -1232,7 +1237,7 @@ describe('node-fetch', () => { const request = new Request(`${base}inspect`, { method: 'POST', body: parameters }) parameters.append('a', '1') return request.text().then(text => { - expect(text).to.equal('') + assert.strictEqual(text, '') }) }) @@ -1248,15 +1253,15 @@ describe('node-fetch', () => { return fetch(url, options).then(res => { return res.json() }).then(res => { - expect(res.method).to.equal('POST') - expect(res.headers['content-type']).to.equal('application/x-www-form-urlencoded;charset=UTF-8') - expect(res.headers['content-length']).to.equal('3') - expect(res.body).to.equal('a=1') + assert.strictEqual(res.method, 'POST') + assert.strictEqual(res.headers['content-type'], 'application/x-www-form-urlencoded;charset=UTF-8') + assert.strictEqual(res.headers['content-length'], '3') + assert.strictEqual(res.body, 'a=1') }) }) it('should still recognize URLSearchParams when extended', () => { - class CustomSearchParameters extends URLSearchParams {} + class CustomSearchParameters extends URLSearchParams { } const parameters = new CustomSearchParameters() parameters.append('a', '1') @@ -1268,10 +1273,10 @@ describe('node-fetch', () => { return fetch(url, options).then(res => { return res.json() }).then(res => { - expect(res.method).to.equal('POST') - expect(res.headers['content-type']).to.equal('application/x-www-form-urlencoded;charset=UTF-8') - expect(res.headers['content-length']).to.equal('3') - expect(res.body).to.equal('a=1') + assert.strictEqual(res.method, 'POST') + assert.strictEqual(res.headers['content-type'], 'application/x-www-form-urlencoded;charset=UTF-8') + assert.strictEqual(res.headers['content-length'], '3') + assert.strictEqual(res.body, 'a=1') }) }) @@ -1284,8 +1289,8 @@ describe('node-fetch', () => { return fetch(url, options).then(res => { return res.json() }).then(res => { - expect(res.method).to.equal('PUT') - expect(res.body).to.equal('a=1') + assert.strictEqual(res.method, 'PUT') + assert.strictEqual(res.body, 'a=1') }) }) @@ -1297,7 +1302,7 @@ describe('node-fetch', () => { return fetch(url, options).then(res => { return res.json() }).then(res => { - expect(res.method).to.equal('DELETE') + assert.strictEqual(res.method, 'DELETE') }) }) @@ -1310,10 +1315,10 @@ describe('node-fetch', () => { return fetch(url, options).then(res => { return res.json() }).then(res => { - expect(res.method).to.equal('DELETE') - expect(res.body).to.equal('a=1') - expect(res.headers['transfer-encoding']).to.be.undefined - expect(res.headers['content-length']).to.equal('3') + assert.strictEqual(res.method, 'DELETE') + assert.strictEqual(res.body, 'a=1') + assert.strictEqual(res.headers['transfer-encoding'], undefined) + assert.strictEqual(res.headers['content-length'], '3') }) }) @@ -1326,8 +1331,8 @@ describe('node-fetch', () => { return fetch(url, options).then(res => { return res.json() }).then(res => { - expect(res.method).to.equal('PATCH') - expect(res.body).to.equal('a=1') + assert.strictEqual(res.method, 'PATCH') + assert.strictEqual(res.body, 'a=1') }) }) @@ -1337,13 +1342,13 @@ describe('node-fetch', () => { method: 'HEAD' } return fetch(url, options).then(res => { - expect(res.status).to.equal(200) - expect(res.statusText).to.equal('OK') - expect(res.headers.get('content-type')).to.equal('text/plain') - // expect(res.body).to.be.an.instanceof(stream.Transform) + assert.strictEqual(res.status, 200) + assert.strictEqual(res.statusText, 'OK') + assert.strictEqual(res.headers.get('content-type'), 'text/plain') + // assert.ok(res.body instanceof stream.Transform) return res.text() }).then(text => { - expect(text).to.equal('') + assert.strictEqual(text, '') }) }) @@ -1353,11 +1358,11 @@ describe('node-fetch', () => { method: 'HEAD' } return fetch(url, options).then(res => { - expect(res.status).to.equal(404) - expect(res.headers.get('content-encoding')).to.equal('gzip') + assert.strictEqual(res.status, 404) + assert.strictEqual(res.headers.get('content-encoding'), 'gzip') return res.text() }).then(text => { - expect(text).to.equal('') + assert.strictEqual(text, '') }) }) @@ -1367,20 +1372,20 @@ describe('node-fetch', () => { method: 'OPTIONS' } return fetch(url, options).then(res => { - expect(res.status).to.equal(200) - expect(res.statusText).to.equal('OK') - expect(res.headers.get('allow')).to.equal('GET, HEAD, OPTIONS') - // expect(res.body).to.be.an.instanceof(stream.Transform) + assert.strictEqual(res.status, 200) + assert.strictEqual(res.statusText, 'OK') + assert.strictEqual(res.headers.get('allow'), 'GET, HEAD, OPTIONS') + // assert.ok(res.body instanceof stream.Transform) }) }) it('should reject decoding body twice', () => { const url = `${base}plain` return fetch(url).then(res => { - expect(res.headers.get('content-type')).to.equal('text/plain') + assert.strictEqual(res.headers.get('content-type'), 'text/plain') return res.text().then(() => { - expect(res.bodyUsed).to.be.true - return expect(res.text()).to.eventually.be.rejectedWith(Error) + assert.strictEqual(res.bodyUsed, true) + return assert.rejects(res.text(), new TypeError('Body is unusable')) }) }) }) @@ -1390,8 +1395,8 @@ describe('node-fetch', () => { return fetch(url).then(res => { const r1 = res.clone() return Promise.all([res.json(), r1.text()]).then(results => { - expect(results[0]).to.deep.equal({ name: 'value' }) - expect(results[1]).to.equal('{"name":"value"}') + assert.deepStrictEqual(results[0], { name: 'value' }) + assert.strictEqual(results[1], '{"name":"value"}') }) }) }) @@ -1401,9 +1406,9 @@ describe('node-fetch', () => { return fetch(url).then(res => { const r1 = res.clone() return res.json().then(result => { - expect(result).to.deep.equal({ name: 'value' }) + assert.deepStrictEqual(result, { name: 'value' }) return r1.text().then(result => { - expect(result).to.equal('{"name":"value"}') + assert.strictEqual(result, '{"name":"value"}') }) }) }) @@ -1414,9 +1419,9 @@ describe('node-fetch', () => { return fetch(url).then(res => { const r1 = res.clone() return r1.text().then(result => { - expect(result).to.equal('{"name":"value"}') + assert.strictEqual(result, '{"name":"value"}') return res.json().then(result => { - expect(result).to.deep.equal({ name: 'value' }) + assert.deepStrictEqual(result, { name: 'value' }) }) }) }) @@ -1426,15 +1431,15 @@ describe('node-fetch', () => { const url = `${base}hello` return fetch(url).then(res => res.text().then(() => { - expect(() => { + assert.throws(() => { res.clone() - }).to.throw(Error) + }, new TypeError('Response.clone: Body has already been consumed.')) }) ) }) - xit('should timeout on cloning response without consuming one of the streams when the second packet size is equal default highWaterMark', function () { - this.timeout(300) + // TODO: fix test. + it.skip('should timeout on cloning response without consuming one of the streams when the second packet size is equal default highWaterMark', { timeout: 300 }, function () { const url = local.mockState(res => { // Observed behavior of TCP packets splitting: // - response body size <= 65438 → single packet sent @@ -1450,8 +1455,8 @@ describe('node-fetch', () => { ).to.timeout }) - xit('should timeout on cloning response without consuming one of the streams when the second packet size is equal custom highWaterMark', function () { - this.timeout(300) + // TODO: fix test. + it.skip('should timeout on cloning response without consuming one of the streams when the second packet size is equal custom highWaterMark', { timeout: 300 }, function () { const url = local.mockState(res => { const firstPacketMaxSize = 65438 const secondPacketSize = 10 @@ -1462,13 +1467,8 @@ describe('node-fetch', () => { ).to.timeout }) - xit('should not timeout on cloning response without consuming one of the streams when the second packet size is less than default highWaterMark', function () { - // TODO: fix test. - if (!isNodeLowerThan('v16.0.0')) { - this.skip() - } - - this.timeout(300) + // TODO: fix test. + it.skip('should not timeout on cloning response without consuming one of the streams when the second packet size is less than default highWaterMark', { timeout: 300 }, async function () { const url = local.mockState(res => { const firstPacketMaxSize = 65438 const secondPacketSize = 16 * 1024 // = defaultHighWaterMark @@ -1479,13 +1479,8 @@ describe('node-fetch', () => { ).not.to.timeout }) - xit('should not timeout on cloning response without consuming one of the streams when the second packet size is less than custom highWaterMark', function () { - // TODO: fix test. - if (!isNodeLowerThan('v16.0.0')) { - this.skip() - } - - this.timeout(300) + // TODO: fix test. + it.skip('should not timeout on cloning response without consuming one of the streams when the second packet size is less than custom highWaterMark', { timeout: 300 }, function () { const url = local.mockState(res => { const firstPacketMaxSize = 65438 const secondPacketSize = 10 @@ -1496,13 +1491,8 @@ describe('node-fetch', () => { ).not.to.timeout }) - xit('should not timeout on cloning response without consuming one of the streams when the response size is double the custom large highWaterMark - 1', function () { - // TODO: fix test. - if (!isNodeLowerThan('v16.0.0')) { - this.skip() - } - - this.timeout(300) + // TODO: fix test. + it.skip('should not timeout on cloning response without consuming one of the streams when the response size is double the custom large highWaterMark - 1', { timeout: 300 }, function () { const url = local.mockState(res => { res.end(crypto.randomBytes((2 * 512 * 1024) - 1)) }) @@ -1511,13 +1501,13 @@ describe('node-fetch', () => { ).not.to.timeout }) - xit('should allow get all responses of a header', () => { - // TODO: fix test. + // TODO: fix test. + it.skip('should allow get all responses of a header', () => { const url = `${base}cookie` return fetch(url).then(res => { const expected = 'a=1, b=1' - expect(res.headers.get('set-cookie')).to.equal(expected) - expect(res.headers.get('Set-Cookie')).to.equal(expected) + assert.strictEqual(res.headers.get('set-cookie'), expected) + assert.strictEqual(res.headers.get('Set-Cookie'), expected) }) }) @@ -1525,9 +1515,9 @@ describe('node-fetch', () => { const url = `${base}hello` const request = new Request(url) return fetch(request).then(res => { - expect(res.url).to.equal(url) - expect(res.ok).to.be.true - expect(res.status).to.equal(200) + assert.strictEqual(res.url, url) + assert.strictEqual(res.ok, true) + assert.strictEqual(res.status, 200) }) }) @@ -1536,9 +1526,9 @@ describe('node-fetch', () => { const urlObject = new URL(url) const request = new Request(urlObject) return fetch(request).then(res => { - expect(res.url).to.equal(url) - expect(res.ok).to.be.true - expect(res.status).to.equal(200) + assert.strictEqual(res.url, url) + assert.strictEqual(res.ok, true) + assert.strictEqual(res.status, 200) }) }) @@ -1547,9 +1537,9 @@ describe('node-fetch', () => { const urlObject = new URL(url) const request = new Request(urlObject) return fetch(request).then(res => { - expect(res.url).to.equal(url) - expect(res.ok).to.be.true - expect(res.status).to.equal(200) + assert.strictEqual(res.url, url) + assert.strictEqual(res.ok, true) + assert.strictEqual(res.status, 200) }) }) @@ -1558,9 +1548,9 @@ describe('node-fetch', () => { const urlObject = new URL(url) const request = new Request(urlObject) return fetch(request).then(res => { - expect(res.url).to.equal(url) - expect(res.ok).to.be.true - expect(res.status).to.equal(200) + assert.strictEqual(res.url, url) + assert.strictEqual(res.ok, true) + assert.strictEqual(res.status, 200) }) }) @@ -1569,7 +1559,7 @@ describe('node-fetch', () => { .blob() .then(blob => blob.text()) .then(body => { - expect(body).to.equal('hello') + assert.strictEqual(body, 'hello') }) }) @@ -1579,7 +1569,7 @@ describe('node-fetch', () => { .then(blob => blob.arrayBuffer()) .then(ab => { const string = String.fromCharCode.apply(null, new Uint8Array(ab)) - expect(string).to.equal('hello') + assert.strictEqual(string, 'hello') }) }) @@ -1598,9 +1588,9 @@ describe('node-fetch', () => { body: blob }) }).then(res => res.json()).then(({ body, headers }) => { - expect(body).to.equal('world') - expect(headers['content-type']).to.equal(type) - expect(headers['content-length']).to.equal(String(length)) + assert.strictEqual(body, 'world') + assert.strictEqual(headers['content-type'], type) + assert.strictEqual(headers['content-length'], String(length)) }) }) @@ -1620,41 +1610,39 @@ describe('node-fetch', () => { }).then(res => { return res.json() }).then(body => { - expect(body.method).to.equal('GET') - expect(body.headers.a).to.equal('2') + assert.strictEqual(body.method, 'GET') + assert.strictEqual(body.headers.a, '2') }) }) - it('should support http request', function () { - this.timeout(5000) + it('should support http request', { timeout: 5000 }, function () { const url = 'https://github.com/' const options = { method: 'HEAD' } return fetch(url, options).then(res => { - expect(res.status).to.equal(200) - expect(res.ok).to.be.true + assert.strictEqual(res.status, 200) + assert.strictEqual(res.ok, true) }) }) it('should encode URLs as UTF-8', async () => { const url = `${base}möbius` const res = await fetch(url) - expect(res.url).to.equal(`${base}m%C3%B6bius`) + assert.strictEqual(res.url, `${base}m%C3%B6bius`) }) - it('should allow manual redirect handling', function () { - this.timeout(5000) + it('should allow manual redirect handling', { timeout: 5000 }, function () { const url = `${base}redirect/302` const options = { redirect: 'manual' } return fetch(url, options).then(res => { - expect(res.status).to.equal(302) - expect(res.url).to.equal(url) - expect(res.type).to.equal('basic') - expect(res.headers.get('Location')).to.equal('/inspect') - expect(res.ok).to.be.false + assert.strictEqual(res.status, 302) + assert.strictEqual(res.url, url) + assert.strictEqual(res.type, 'basic') + assert.strictEqual(res.headers.get('Location'), '/inspect') + assert.strictEqual(res.ok, false) }) }) }) diff --git a/test/node-fetch/mock.js b/test/node-fetch/mock.js index a53f464a1a9..f9835484328 100644 --- a/test/node-fetch/mock.js +++ b/test/node-fetch/mock.js @@ -1,7 +1,8 @@ -/* eslint no-unused-expressions: "off" */ +'use strict' // Test tools -const chai = require('chai') +const assert = require('node:assert') +const { describe, it } = require('node:test') const { fetch, @@ -10,8 +11,6 @@ const { Headers } = require('../../index.js') -const { expect } = chai - describe('node-fetch with MockAgent', () => { it('should match the url', async () => { const mockAgent = new MockAgent() @@ -30,8 +29,8 @@ describe('node-fetch with MockAgent', () => { method: 'GET' }) - expect(res.status).to.equal(200) - expect(await res.json()).to.deep.equal({ success: true }) + assert.strictEqual(res.status, 200) + assert.deepStrictEqual(await res.json(), { success: true }) }) it('should match the body', async () => { @@ -55,8 +54,8 @@ describe('node-fetch with MockAgent', () => { body: 'request body' }) - expect(res.status).to.equal(200) - expect(await res.json()).to.deep.equal({ success: true }) + assert.strictEqual(res.status, 200) + assert.deepStrictEqual(await res.json(), { success: true }) }) it('should match the headers', async () => { @@ -79,8 +78,9 @@ describe('node-fetch with MockAgent', () => { method: 'GET', headers: new Headers({ 'User-Agent': 'undici' }) }) - expect(res.status).to.equal(200) - expect(await res.json()).to.deep.equal({ success: true }) + + assert.strictEqual(res.status, 200) + assert.deepStrictEqual(await res.json(), { success: true }) }) it('should match the headers with a matching function', async () => { @@ -93,8 +93,8 @@ describe('node-fetch with MockAgent', () => { path: '/test', method: 'GET', headers (headers) { - expect(headers).to.be.an('object') - expect(headers).to.have.property('user-agent', 'undici') + assert.strictEqual(typeof headers, 'object') + assert.strictEqual(headers['user-agent'], 'undici') return true } }) @@ -106,7 +106,7 @@ describe('node-fetch with MockAgent', () => { headers: new Headers({ 'User-Agent': 'undici' }) }) - expect(res.status).to.equal(200) - expect(await res.json()).to.deep.equal({ success: true }) + assert.strictEqual(res.status, 200) + assert.deepStrictEqual(await res.json(), { success: true }) }) }) diff --git a/test/node-fetch/request.js b/test/node-fetch/request.js index 405bc2c467e..505dcc3eebb 100644 --- a/test/node-fetch/request.js +++ b/test/node-fetch/request.js @@ -1,14 +1,14 @@ +'use strict' + +const assert = require('node:assert') +const { describe, it, before, after } = require('node:test') const stream = require('node:stream') const http = require('node:http') - -const chai = require('chai') const { Blob } = require('node:buffer') -const Request = require('../../lib/fetch/request.js').Request +const { Request } = require('../../index.js') const TestServer = require('./utils/server.js') -const { expect } = chai - describe('Request', () => { const local = new TestServer() let base @@ -43,47 +43,47 @@ describe('Request', () => { 'clone', 'signal' ]) { - expect(enumerableProperties).to.contain(toCheck) + assert.ok(enumerableProperties.includes(toCheck)) } - // for (const toCheck of [ - // 'body', 'bodyUsed', 'method', 'url', 'headers', 'redirect', 'signal' - // ]) { - // expect(() => { - // request[toCheck] = 'abc' - // }).to.throw() - // } + for (const toCheck of [ + 'body', 'bodyUsed', 'method', 'url', 'headers', 'redirect', 'signal' + ]) { + assert.throws(() => { + request[toCheck] = 'abc' + }, new TypeError(`Cannot set property ${toCheck} of # which has only a getter`)) + } }) - // it('should support wrapping Request instance', () => { - // const url = `${base}hello` + it.skip('should support wrapping Request instance', () => { + const url = `${base}hello` - // const form = new FormData() - // form.append('a', '1') - // const { signal } = new AbortController() + const form = new FormData() + form.append('a', '1') + const { signal } = new AbortController() - // const r1 = new Request(url, { - // method: 'POST', - // follow: 1, - // body: form, - // signal - // }) - // const r2 = new Request(r1, { - // follow: 2 - // }) + const r1 = new Request(url, { + method: 'POST', + follow: 1, + body: form, + signal + }) + const r2 = new Request(r1, { + follow: 2 + }) - // expect(r2.url).to.equal(url) - // expect(r2.method).to.equal('POST') - // expect(r2.signal).to.equal(signal) - // // Note that we didn't clone the body - // expect(r2.body).to.equal(form) - // expect(r1.follow).to.equal(1) - // expect(r2.follow).to.equal(2) - // expect(r1.counter).to.equal(0) - // expect(r2.counter).to.equal(0) - // }) + assert.strictEqual(r2.url, url) + assert.strictEqual(r2.method, 'POST') + assert.strictEqual(r2.signal[Symbol.toStringTag], 'AbortSignal') + // Note that we didn't clone the body + assert.strictEqual(r2.body, form) + assert.strictEqual(r1.follow, 1) + assert.strictEqual(r2.follow, 2) + assert.strictEqual(r1.counter, 0) + assert.strictEqual(r2.counter, 0) + }) - xit('should override signal on derived Request instances', () => { + it.skip('should override signal on derived Request instances', () => { const parentAbortController = new AbortController() const derivedAbortController = new AbortController() const parentRequest = new Request(`${base}hello`, { @@ -92,11 +92,11 @@ describe('Request', () => { const derivedRequest = new Request(parentRequest, { signal: derivedAbortController.signal }) - expect(parentRequest.signal).to.equal(parentAbortController.signal) - expect(derivedRequest.signal).to.equal(derivedAbortController.signal) + assert.strictEqual(parentRequest.signal, parentAbortController.signal) + assert.strictEqual(derivedRequest.signal, derivedAbortController.signal) }) - xit('should allow removing signal on derived Request instances', () => { + it.skip('should allow removing signal on derived Request instances', () => { const parentAbortController = new AbortController() const parentRequest = new Request(`${base}hello`, { signal: parentAbortController.signal @@ -104,29 +104,25 @@ describe('Request', () => { const derivedRequest = new Request(parentRequest, { signal: null }) - expect(parentRequest.signal).to.equal(parentAbortController.signal) - expect(derivedRequest.signal).to.equal(null) + assert.strictEqual(parentRequest.signal, parentAbortController.signal) + assert.strictEqual(derivedRequest.signal, null) }) it('should throw error with GET/HEAD requests with body', () => { - expect(() => new Request(base, { body: '' })) - .to.throw(TypeError) - expect(() => new Request(base, { body: 'a' })) - .to.throw(TypeError) - expect(() => new Request(base, { body: '', method: 'HEAD' })) - .to.throw(TypeError) - expect(() => new Request(base, { body: 'a', method: 'HEAD' })) - .to.throw(TypeError) - expect(() => new Request(base, { body: 'a', method: 'get' })) - .to.throw(TypeError) - expect(() => new Request(base, { body: 'a', method: 'head' })) - .to.throw(TypeError) + assert.throws(() => new Request(base, { body: '' }), new TypeError('Request with GET/HEAD method cannot have body.')) + assert.throws(() => new Request(base, { body: 'a' }), new TypeError('Request with GET/HEAD method cannot have body.')) + assert.throws(() => new Request(base, { body: '', method: 'HEAD' }), new TypeError('Request with GET/HEAD method cannot have body.')) + assert.throws(() => new Request(base, { body: 'a', method: 'HEAD' }), new TypeError('Request with GET/HEAD method cannot have body.')) + assert.throws(() => new Request(base, { body: '', method: 'get' }), new TypeError('Request with GET/HEAD method cannot have body.')) + assert.throws(() => new Request(base, { body: 'a', method: 'get' }), new TypeError('Request with GET/HEAD method cannot have body.')) + assert.throws(() => new Request(base, { body: '', method: 'head' }), new TypeError('Request with GET/HEAD method cannot have body.')) + assert.throws(() => new Request(base, { body: 'a', method: 'head' }), new TypeError('Request with GET/HEAD method cannot have body.')) }) it('should default to null as body', () => { const request = new Request(base) - expect(request.body).to.equal(null) - return request.text().then(result => expect(result).to.equal('')) + assert.strictEqual(request.body, null) + return request.text().then(result => assert.strictEqual(result, '')) }) it('should support parsing headers', () => { @@ -136,8 +132,8 @@ describe('Request', () => { a: '1' } }) - expect(request.url).to.equal(url) - expect(request.headers.get('a')).to.equal('1') + assert.strictEqual(request.url, url) + assert.strictEqual(request.headers.get('a'), '1') }) it('should support arrayBuffer() method', () => { @@ -146,11 +142,11 @@ describe('Request', () => { method: 'POST', body: 'a=1' }) - expect(request.url).to.equal(url) + assert.strictEqual(request.url, url) return request.arrayBuffer().then(result => { - expect(result).to.be.an.instanceOf(ArrayBuffer) + assert.ok(result instanceof ArrayBuffer) const string = String.fromCharCode.apply(null, new Uint8Array(result)) - expect(string).to.equal('a=1') + assert.strictEqual(string, 'a=1') }) }) @@ -160,9 +156,9 @@ describe('Request', () => { method: 'POST', body: 'a=1' }) - expect(request.url).to.equal(url) + assert.strictEqual(request.url, url) return request.text().then(result => { - expect(result).to.equal('a=1') + assert.strictEqual(result, 'a=1') }) }) @@ -172,9 +168,9 @@ describe('Request', () => { method: 'POST', body: '{"a":1}' }) - expect(request.url).to.equal(url) + assert.strictEqual(request.url, url) return request.json().then(result => { - expect(result.a).to.equal(1) + assert.strictEqual(result.a, 1) }) }) @@ -184,11 +180,11 @@ describe('Request', () => { method: 'POST', body: Buffer.from('a=1') }) - expect(request.url).to.equal(url) + assert.strictEqual(request.url, url) return request.blob().then(result => { - expect(result).to.be.an.instanceOf(Blob) - expect(result.size).to.equal(3) - expect(result.type).to.equal('') + assert.ok(result instanceof Blob) + assert.strictEqual(result.size, 3) + assert.strictEqual(result.type, '') }) }) @@ -211,16 +207,16 @@ describe('Request', () => { duplex: 'half' }) const cl = request.clone() - expect(cl.url).to.equal(url) - expect(cl.method).to.equal('POST') - expect(cl.redirect).to.equal('manual') - expect(cl.headers.get('b')).to.equal('2') - expect(cl.method).to.equal('POST') + assert.strictEqual(cl.url, url) + assert.strictEqual(cl.method, 'POST') + assert.strictEqual(cl.redirect, 'manual') + assert.strictEqual(cl.headers.get('b'), '2') + assert.strictEqual(cl.method, 'POST') // Clone body shouldn't be the same body - expect(cl.body).to.not.equal(body) + assert.notDeepEqual(cl.body, body) return Promise.all([cl.text(), request.text()]).then(results => { - expect(results[0]).to.equal('a=1') - expect(results[1]).to.equal('a=1') + assert.strictEqual(results[0], 'a=1') + assert.strictEqual(results[1], 'a=1') }) }) @@ -233,7 +229,7 @@ describe('Request', () => { }) new Uint8Array(body)[0] = 0 return request.text().then(result => { - expect(result).to.equal('a=12345678901234') + assert.strictEqual(result, 'a=12345678901234') }) }) @@ -247,7 +243,7 @@ describe('Request', () => { }) body[0] = 0 return request.text().then(result => { - expect(result).to.equal('123456789') + assert.strictEqual(result, '123456789') }) }) @@ -261,7 +257,7 @@ describe('Request', () => { }) body[0] = 0n return request.text().then(result => { - expect(result).to.equal('78901234') + assert.strictEqual(result, '78901234') }) }) @@ -275,7 +271,7 @@ describe('Request', () => { }) body[0] = 0 return request.text().then(result => { - expect(result).to.equal('123456789') + assert.strictEqual(result, '123456789') }) }) }) diff --git a/test/node-fetch/response.js b/test/node-fetch/response.js index b928651edfa..e28dcb37119 100644 --- a/test/node-fetch/response.js +++ b/test/node-fetch/response.js @@ -1,14 +1,13 @@ -/* eslint no-unused-expressions: "off" */ +'use strict' -const chai = require('chai') +const assert = require('node:assert') +const { describe, it, before, after } = require('node:test') const stream = require('node:stream') -const { Response } = require('../../lib/fetch/response.js') +const { Response } = require('../../index.js') const TestServer = require('./utils/server.js') const { Blob } = require('node:buffer') const { kState } = require('../../lib/fetch/symbols.js') -const { expect } = chai - describe('Response', () => { const local = new TestServer() let base @@ -45,32 +44,30 @@ describe('Response', () => { 'headers', 'clone' ]) { - expect(enumerableProperties).to.contain(toCheck) + assert.ok(enumerableProperties.includes(toCheck)) } - // TODO - // for (const toCheck of [ - // 'body', - // 'bodyUsed', - // 'type', - // 'url', - // 'status', - // 'ok', - // 'redirected', - // 'statusText', - // 'headers' - // ]) { - // expect(() => { - // res[toCheck] = 'abc' - // }).to.throw() - // } - }) - - it('should support empty options', () => { + for (const toCheck of [ + 'body', + 'bodyUsed', + 'type', + 'url', + 'status', + 'ok', + 'redirected', + 'statusText', + 'headers' + ]) { + assert.throws(() => { + res[toCheck] = 'abc' + }, new TypeError(`Cannot set property ${toCheck} of # which has only a getter`)) + } + }) + + it('should support empty options', async () => { const res = new Response(stream.Readable.from('a=1')) - return res.text().then(result => { - expect(result).to.equal('a=1') - }) + const result = await res.text() + assert.strictEqual(result, 'a=1') }) it('should support parsing headers', () => { @@ -79,36 +76,33 @@ describe('Response', () => { a: '1' } }) - expect(res.headers.get('a')).to.equal('1') + assert.strictEqual(res.headers.get('a'), '1') }) - it('should support text() method', () => { + it('should support text() method', async () => { const res = new Response('a=1') - return res.text().then(result => { - expect(result).to.equal('a=1') - }) + const result = await res.text() + assert.strictEqual(result, 'a=1') }) - it('should support json() method', () => { + it('should support json() method', async () => { const res = new Response('{"a":1}') - return res.json().then(result => { - expect(result.a).to.equal(1) - }) + const result = await res.json() + assert.deepStrictEqual(result, { a: 1 }) }) if (Blob) { - it('should support blob() method', () => { + it('should support blob() method', async () => { const res = new Response('a=1', { method: 'POST', headers: { 'Content-Type': 'text/plain' } }) - return res.blob().then(result => { - expect(result).to.be.an.instanceOf(Blob) - expect(result.size).to.equal(3) - expect(result.type).to.equal('text/plain') - }) + const result = await res.blob() + assert.ok(result instanceof Blob) + assert.strictEqual(result.size, 3) + assert.strictEqual(result.type, 'text/plain') }) } @@ -123,129 +117,129 @@ describe('Response', () => { }) res[kState].urlList = [new URL(base)] const cl = res.clone() - expect(cl.headers.get('a')).to.equal('1') - expect(cl.type).to.equal('default') - expect(cl.url).to.equal(base) - expect(cl.status).to.equal(346) - expect(cl.statusText).to.equal('production') - expect(cl.ok).to.be.false + assert.strictEqual(cl.headers.get('a'), '1') + assert.strictEqual(cl.type, 'default') + assert.strictEqual(cl.url, base) + assert.strictEqual(cl.status, 346) + assert.strictEqual(cl.statusText, 'production') + assert.strictEqual(cl.ok, false) // Clone body shouldn't be the same body - expect(cl.body).to.not.equal(body) + assert.notStrictEqual(cl.body, body) return Promise.all([cl.text(), res.text()]).then(results => { - expect(results[0]).to.equal('a=1') - expect(results[1]).to.equal('a=1') + assert.strictEqual(results[0], 'a=1') + assert.strictEqual(results[1], 'a=1') }) }) - it('should support stream as body', () => { + it('should support stream as body', async () => { const body = stream.Readable.from('a=1') const res = new Response(body) - return res.text().then(result => { - expect(result).to.equal('a=1') - }) + const result = await res.text() + + assert.strictEqual(result, 'a=1') }) - it('should support string as body', () => { + it('should support string as body', async () => { const res = new Response('a=1') - return res.text().then(result => { - expect(result).to.equal('a=1') - }) + const result = await res.text() + + assert.strictEqual(result, 'a=1') }) - it('should support buffer as body', () => { + it('should support buffer as body', async () => { const res = new Response(Buffer.from('a=1')) - return res.text().then(result => { - expect(result).to.equal('a=1') - }) + const result = await res.text() + + assert.strictEqual(result, 'a=1') }) - it('should support ArrayBuffer as body', () => { + it('should support ArrayBuffer as body', async () => { const encoder = new TextEncoder() const fullbuffer = encoder.encode('a=12345678901234').buffer const res = new Response(fullbuffer) new Uint8Array(fullbuffer)[0] = 0 - return res.text().then(result => { - expect(result).to.equal('a=12345678901234') - }) + + const result = await res.text() + assert.strictEqual(result, 'a=12345678901234') }) it('should support blob as body', async () => { const res = new Response(new Blob(['a=1'])) - return res.text().then(result => { - expect(result).to.equal('a=1') - }) + const result = await res.text() + + assert.strictEqual(result, 'a=1') }) - it('should support Uint8Array as body', () => { + it('should support Uint8Array as body', async () => { const encoder = new TextEncoder() const fullbuffer = encoder.encode('a=12345678901234').buffer const body = new Uint8Array(fullbuffer, 2, 9) const res = new Response(body) body[0] = 0 - return res.text().then(result => { - expect(result).to.equal('123456789') - }) + + const result = await res.text() + assert.strictEqual(result, '123456789') }) - it('should support BigUint64Array as body', () => { + it('should support BigUint64Array as body', async () => { const encoder = new TextEncoder() const fullbuffer = encoder.encode('a=12345678901234').buffer const body = new BigUint64Array(fullbuffer, 8, 1) const res = new Response(body) body[0] = 0n - return res.text().then(result => { - expect(result).to.equal('78901234') - }) + + const result = await res.text() + assert.strictEqual(result, '78901234') }) - it('should support DataView as body', () => { + it('should support DataView as body', async () => { const encoder = new TextEncoder() const fullbuffer = encoder.encode('a=12345678901234').buffer const body = new Uint8Array(fullbuffer, 2, 9) const res = new Response(body) body[0] = 0 - return res.text().then(result => { - expect(result).to.equal('123456789') - }) + + const result = await res.text() + assert.strictEqual(result, '123456789') }) it('should default to null as body', () => { const res = new Response() - expect(res.body).to.equal(null) + assert.strictEqual(res.body, null) - return res.text().then(result => expect(result).to.equal('')) + return res.text().then(result => assert.strictEqual(result, '')) }) it('should default to 200 as status code', () => { const res = new Response(null) - expect(res.status).to.equal(200) + assert.strictEqual(res.status, 200) }) it('should default to empty string as url', () => { const res = new Response() - expect(res.url).to.equal('') + assert.strictEqual(res.url, '') }) it('should support error() static method', () => { const res = Response.error() - expect(res).to.be.an.instanceof(Response) - expect(res.type).to.equal('error') - expect(res.status).to.equal(0) - expect(res.statusText).to.equal('') + assert.ok(res instanceof Response) + assert.strictEqual(res.status, 0) + assert.strictEqual(res.statusText, '') + assert.strictEqual(res.type, 'error') }) it('should support undefined status', () => { const res = new Response(null, { status: undefined }) - expect(res.status).to.equal(200) + assert.strictEqual(res.status, 200) }) it('should support undefined statusText', () => { const res = new Response(null, { statusText: undefined }) - expect(res.statusText).to.equal('') + assert.strictEqual(res.statusText, '') }) it('should not set bodyUsed to undefined', () => { const res = new Response() - expect(res.bodyUsed).to.be.false + assert.strictEqual(res.bodyUsed, false) }) }) diff --git a/test/node-fetch/utils/chai-timeout.js b/test/node-fetch/utils/chai-timeout.js deleted file mode 100644 index 6838a4cc322..00000000000 --- a/test/node-fetch/utils/chai-timeout.js +++ /dev/null @@ -1,15 +0,0 @@ -const pTimeout = require('p-timeout') - -module.exports = ({ Assertion }, utils) => { - utils.addProperty(Assertion.prototype, 'timeout', async function () { - let timeouted = false - await pTimeout(this._obj, 150, () => { - timeouted = true - }) - return this.assert( - timeouted, - 'expected promise to timeout but it was resolved', - 'expected promise not to timeout but it timed out' - ) - }) -} diff --git a/test/node-test/client-dispatch.js b/test/node-test/client-dispatch.js index b89bad7278b..c9fc125bb60 100644 --- a/test/node-test/client-dispatch.js +++ b/test/node-test/client-dispatch.js @@ -4,7 +4,6 @@ const { test } = require('node:test') const assert = require('node:assert/strict') const http = require('node:http') const { Client, Pool, errors } = require('../..') -const stream = require('node:stream') const { createSecureServer } = require('node:http2') const pem = require('https-pem') const { tspl } = require('@matteo.collina/tspl') @@ -678,183 +677,6 @@ test('dispatch pool onError missing', async (t) => { await p.completed }) -test('dispatch onBodySent not a function', async (t) => { - const p = tspl(t, { plan: 2 }) - const server = http.createServer((req, res) => { - res.end('ad') - }) - t.after(closeServerAsPromise(server)) - - server.listen(0, () => { - const client = new Pool(`http://localhost:${server.address().port}`) - t.after(() => { return client.close() }) - - client.dispatch({ - path: '/', - method: 'GET' - }, { - onBodySent: '42', - onConnect () {}, - onHeaders () {}, - onData () {}, - onError (err) { - p.strictEqual(err.code, 'UND_ERR_INVALID_ARG') - p.strictEqual(err.message, 'invalid onBodySent method') - } - }) - }) - - await p.completed -}) - -test('dispatch onBodySent buffer', async (t) => { - const p = tspl(t, { plan: 3 }) - - const server = http.createServer((req, res) => { - res.end('ad') - }) - t.after(closeServerAsPromise(server)) - - server.listen(0, () => { - const client = new Pool(`http://localhost:${server.address().port}`) - t.after(() => { return client.close() }) - const body = 'hello 🚀' - client.dispatch({ - path: '/', - method: 'POST', - body - }, { - onBodySent (chunk) { - p.strictEqual(chunk.toString(), body) - }, - onRequestSent () { - p.ok(1) - }, - onError (err) { - throw err - }, - onConnect () {}, - onHeaders () {}, - onData () {}, - onComplete () { - p.ok(1) - } - }) - }) - - await p.completed -}) - -test('dispatch onBodySent stream', async (t) => { - const p = tspl(t, { plan: 8 }) - const server = http.createServer((req, res) => { - res.end('ad') - }) - t.after(closeServerAsPromise(server)) - const chunks = ['he', 'llo', 'world', '🚀'] - const toSendBytes = chunks.reduce((a, b) => a + Buffer.byteLength(b), 0) - const body = stream.Readable.from(chunks) - server.listen(0, () => { - const client = new Pool(`http://localhost:${server.address().port}`) - t.after(() => { return client.close() }) - let sentBytes = 0 - let currentChunk = 0 - client.dispatch({ - path: '/', - method: 'POST', - body - }, { - onBodySent (chunk) { - p.strictEqual(chunks[currentChunk++], chunk) - sentBytes += Buffer.byteLength(chunk) - }, - onRequestSent () { - p.ok(1) - }, - onError (err) { - throw err - }, - onConnect () {}, - onHeaders () {}, - onData () {}, - onComplete () { - p.strictEqual(currentChunk, chunks.length) - p.strictEqual(sentBytes, toSendBytes) - p.ok(1) - } - }) - }) - - await p.completed -}) - -test('dispatch onBodySent async-iterable', (t, done) => { - const server = http.createServer((req, res) => { - res.end('ad') - }) - t.after(closeServerAsPromise(server)) - const chunks = ['he', 'llo', 'world', '🚀'] - const toSendBytes = chunks.reduce((a, b) => a + Buffer.byteLength(b), 0) - server.listen(0, () => { - const client = new Pool(`http://localhost:${server.address().port}`) - t.after(() => { return client.close() }) - let sentBytes = 0 - let currentChunk = 0 - client.dispatch({ - path: '/', - method: 'POST', - body: chunks - }, { - onBodySent (chunk) { - assert.strictEqual(chunks[currentChunk++], chunk) - sentBytes += Buffer.byteLength(chunk) - }, - onError (err) { - throw err - }, - onConnect () {}, - onHeaders () {}, - onData () {}, - onComplete () { - assert.strictEqual(currentChunk, chunks.length) - assert.strictEqual(sentBytes, toSendBytes) - done() - } - }) - }) -}) - -test('dispatch onBodySent throws error', (t, done) => { - const server = http.createServer((req, res) => { - res.end('ended') - }) - t.after(closeServerAsPromise(server)) - - server.listen(0, () => { - const client = new Pool(`http://localhost:${server.address().port}`) - t.after(() => { return client.close() }) - const body = 'hello' - client.dispatch({ - path: '/', - method: 'POST', - body - }, { - onBodySent (chunk) { - throw new Error('fail') - }, - onError (err) { - assert.ok(err instanceof Error) - assert.strictEqual(err.message, 'fail') - done() - }, - onConnect () {}, - onHeaders () {}, - onData () {}, - onComplete () {} - }) - }) -}) - test('dispatches in expected order', async (t) => { const server = http.createServer((req, res) => { res.end('ended') @@ -878,12 +700,6 @@ test('dispatches in expected order', async (t) => { onConnect () { dispatches.push('onConnect') }, - onBodySent () { - dispatches.push('onBodySent') - }, - onResponseStarted () { - dispatches.push('onResponseStarted') - }, onHeaders () { dispatches.push('onHeaders') }, @@ -892,7 +708,7 @@ test('dispatches in expected order', async (t) => { }, onComplete () { dispatches.push('onComplete') - p.deepStrictEqual(dispatches, ['onConnect', 'onBodySent', 'onResponseStarted', 'onHeaders', 'onData', 'onComplete']) + p.deepStrictEqual(dispatches, ['onConnect', 'onHeaders', 'onData', 'onComplete']) }, onError (err) { p.ifError(err) @@ -935,12 +751,6 @@ test('dispatches in expected order for http2', async (t) => { onConnect () { dispatches.push('onConnect') }, - onBodySent () { - dispatches.push('onBodySent') - }, - onResponseStarted () { - dispatches.push('onResponseStarted') - }, onHeaders () { dispatches.push('onHeaders') }, @@ -949,7 +759,7 @@ test('dispatches in expected order for http2', async (t) => { }, onComplete () { dispatches.push('onComplete') - p.deepStrictEqual(dispatches, ['onConnect', 'onBodySent', 'onResponseStarted', 'onHeaders', 'onData', 'onComplete']) + p.deepStrictEqual(dispatches, ['onConnect', 'onHeaders', 'onData', 'onComplete']) }, onError (err) { p.ifError(err) diff --git a/test/node-test/util.js b/test/node-test/util.js index 9d18f98d596..ce15efb4a48 100644 --- a/test/node-test/util.js +++ b/test/node-test/util.js @@ -41,25 +41,17 @@ test('validateHandler', () => { assert.throws(() => util.validateHandler({ onConnect: () => {}, onError: () => {}, - onBodySent: null - }), InvalidArgumentError, 'invalid onBodySent method') - assert.throws(() => util.validateHandler({ - onConnect: () => {}, - onError: () => {}, - onBodySent: () => {}, onHeaders: null }), InvalidArgumentError, 'invalid onHeaders method') assert.throws(() => util.validateHandler({ onConnect: () => {}, onError: () => {}, - onBodySent: () => {}, onHeaders: () => {}, onData: null }), InvalidArgumentError, 'invalid onData method') assert.throws(() => util.validateHandler({ onConnect: () => {}, onError: () => {}, - onBodySent: () => {}, onHeaders: () => {}, onData: () => {}, onComplete: null @@ -67,13 +59,11 @@ test('validateHandler', () => { assert.throws(() => util.validateHandler({ onConnect: () => {}, onError: () => {}, - onBodySent: () => {}, onUpgrade: 'null' }, 'CONNECT'), InvalidArgumentError, 'invalid onUpgrade method') assert.throws(() => util.validateHandler({ onConnect: () => {}, onError: () => {}, - onBodySent: () => {}, onUpgrade: 'null' }, 'CONNECT', () => {}), InvalidArgumentError, 'invalid onUpgrade method') }) diff --git a/test/retry-handler.js b/test/retry-handler.js index d90ff5ebc45..dd56adee1a9 100644 --- a/test/retry-handler.js +++ b/test/retry-handler.js @@ -62,9 +62,6 @@ tap.test('Should retry status code', t => { onConnect () { t.pass() }, - onBodySent () { - t.pass() - }, onHeaders (status, _rawHeaders, resume, _statusMessage) { t.equal(status, 200) return true @@ -147,9 +144,6 @@ tap.test('Should use retry-after header for retries', t => { onConnect () { t.pass() }, - onBodySent () { - t.pass() - }, onHeaders (status, _rawHeaders, resume, _statusMessage) { t.equal(status, 200) return true @@ -233,9 +227,6 @@ tap.test('Should use retry-after header for retries (date)', t => { onConnect () { t.pass() }, - onBodySent () { - t.pass() - }, onHeaders (status, _rawHeaders, resume, _statusMessage) { t.equal(status, 200) return true @@ -316,9 +307,6 @@ tap.test('Should retry with defaults', t => { onConnect () { t.pass() }, - onBodySent () { - t.pass() - }, onHeaders (status, _rawHeaders, resume, _statusMessage) { t.equal(status, 200) return true @@ -401,7 +389,7 @@ tap.test('Should handle 206 partial content', t => { } } - t.plan(8) + t.plan(6) server.listen(0, () => { const client = new Client(`http://localhost:${server.address().port}`) @@ -410,15 +398,9 @@ tap.test('Should handle 206 partial content', t => { return client.dispatch(...args) }, handler: { - onRequestSent () { - t.pass() - }, onConnect () { t.pass() }, - onBodySent () { - t.pass() - }, onHeaders (status, _rawHeaders, resume, _statusMessage) { t.equal(status, 200) return true @@ -502,9 +484,6 @@ tap.test('Should handle 206 partial content - bad-etag', t => { onConnect () { t.pass() }, - onBodySent () { - t.pass() - }, onHeaders (status, _rawHeaders, resume, _statusMessage) { t.pass() return true @@ -649,9 +628,6 @@ tap.test('should not error if request is not meant to be retried', t => { onConnect () { t.pass() }, - onBodySent () { - t.pass() - }, onHeaders (status, _rawHeaders, resume, _statusMessage) { t.equal(status, 400) return true diff --git a/types/dispatcher.d.ts b/types/dispatcher.d.ts index 0872df0fc0b..943264285be 100644 --- a/types/dispatcher.d.ts +++ b/types/dispatcher.d.ts @@ -217,16 +217,12 @@ declare namespace Dispatcher { onError?(err: Error): void; /** Invoked when request is upgraded either due to a `Upgrade` header or `CONNECT` method. */ onUpgrade?(statusCode: number, headers: Buffer[] | string[] | null, socket: Duplex): void; - /** Invoked when response is received, before headers have been read. **/ - onResponseStarted?(): void; /** Invoked when statusCode and headers have been received. May be invoked multiple times due to 1xx informational headers. */ onHeaders?(statusCode: number, headers: Buffer[] | string[] | null, resume: () => void, statusText: string): boolean; /** Invoked when response payload data is received. */ onData?(chunk: Buffer): boolean; /** Invoked when response payload and trailers have been received and the request has completed. */ onComplete?(trailers: string[] | null): void; - /** Invoked when a body chunk is sent to the server. May be invoked multiple times for chunked requests */ - onBodySent?(chunkSize: number, totalBytesSent: number): void; } export type PipelineHandler = (data: PipelineHandlerData) => Readable; export type HttpMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'CONNECT' | 'OPTIONS' | 'TRACE' | 'PATCH';