298 lines
7.7 KiB
JavaScript
298 lines
7.7 KiB
JavaScript
import stream from 'node:stream';
|
|
import http from 'node:http';
|
|
|
|
import AbortController from 'abort-controller';
|
|
import chai from 'chai';
|
|
import FormData from 'form-data';
|
|
import Blob from 'fetch-blob';
|
|
|
|
import {Request} from '../src/index.js';
|
|
import TestServer from './utils/server.js';
|
|
|
|
const {expect} = chai;
|
|
|
|
describe('Request', () => {
|
|
const local = new TestServer();
|
|
let base;
|
|
|
|
before(async () => {
|
|
await local.start();
|
|
base = `http://${local.hostname}:${local.port}/`;
|
|
});
|
|
|
|
after(async () => {
|
|
return local.stop();
|
|
});
|
|
|
|
it('should have attributes conforming to Web IDL', () => {
|
|
const request = new Request('https://github.com/');
|
|
const enumerableProperties = [];
|
|
for (const property in request) {
|
|
enumerableProperties.push(property);
|
|
}
|
|
|
|
for (const toCheck of [
|
|
'body',
|
|
'bodyUsed',
|
|
'arrayBuffer',
|
|
'blob',
|
|
'json',
|
|
'text',
|
|
'method',
|
|
'url',
|
|
'headers',
|
|
'redirect',
|
|
'clone',
|
|
'signal'
|
|
]) {
|
|
expect(enumerableProperties).to.contain(toCheck);
|
|
}
|
|
|
|
for (const toCheck of [
|
|
'body', 'bodyUsed', 'method', 'url', 'headers', 'redirect', 'signal'
|
|
]) {
|
|
expect(() => {
|
|
request[toCheck] = 'abc';
|
|
}).to.throw();
|
|
}
|
|
});
|
|
|
|
it('should support wrapping Request instance', () => {
|
|
const url = `${base}hello`;
|
|
|
|
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
|
|
});
|
|
|
|
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);
|
|
});
|
|
|
|
it('should override signal on derived Request instances', () => {
|
|
const parentAbortController = new AbortController();
|
|
const derivedAbortController = new AbortController();
|
|
const parentRequest = new Request(`${base}hello`, {
|
|
signal: parentAbortController.signal
|
|
});
|
|
const derivedRequest = new Request(parentRequest, {
|
|
signal: derivedAbortController.signal
|
|
});
|
|
expect(parentRequest.signal).to.equal(parentAbortController.signal);
|
|
expect(derivedRequest.signal).to.equal(derivedAbortController.signal);
|
|
});
|
|
|
|
it('should allow removing signal on derived Request instances', () => {
|
|
const parentAbortController = new AbortController();
|
|
const parentRequest = new Request(`${base}hello`, {
|
|
signal: parentAbortController.signal
|
|
});
|
|
const derivedRequest = new Request(parentRequest, {
|
|
signal: null
|
|
});
|
|
expect(parentRequest.signal).to.equal(parentAbortController.signal);
|
|
expect(derivedRequest.signal).to.equal(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);
|
|
expect(() => new Request(new Request(base), {body: 'a'}))
|
|
.to.throw(TypeError);
|
|
});
|
|
|
|
it('should throw error when including credentials', () => {
|
|
expect(() => new Request('https://john:[email protected]/'))
|
|
.to.throw(TypeError);
|
|
expect(() => new Request(new URL('https://john:[email protected]/')))
|
|
.to.throw(TypeError);
|
|
});
|
|
|
|
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(''));
|
|
});
|
|
|
|
it('should support parsing headers', () => {
|
|
const url = base;
|
|
const request = new Request(url, {
|
|
headers: {
|
|
a: '1'
|
|
}
|
|
});
|
|
expect(request.url).to.equal(url);
|
|
expect(request.headers.get('a')).to.equal('1');
|
|
});
|
|
|
|
it('should support arrayBuffer() method', () => {
|
|
const url = base;
|
|
const request = new Request(url, {
|
|
method: 'POST',
|
|
body: 'a=1'
|
|
});
|
|
expect(request.url).to.equal(url);
|
|
return request.arrayBuffer().then(result => {
|
|
expect(result).to.be.an.instanceOf(ArrayBuffer);
|
|
const string = String.fromCharCode.apply(null, new Uint8Array(result));
|
|
expect(string).to.equal('a=1');
|
|
});
|
|
});
|
|
|
|
it('should support text() method', () => {
|
|
const url = base;
|
|
const request = new Request(url, {
|
|
method: 'POST',
|
|
body: 'a=1'
|
|
});
|
|
expect(request.url).to.equal(url);
|
|
return request.text().then(result => {
|
|
expect(result).to.equal('a=1');
|
|
});
|
|
});
|
|
|
|
it('should support json() method', () => {
|
|
const url = base;
|
|
const request = new Request(url, {
|
|
method: 'POST',
|
|
body: '{"a":1}'
|
|
});
|
|
expect(request.url).to.equal(url);
|
|
return request.json().then(result => {
|
|
expect(result.a).to.equal(1);
|
|
});
|
|
});
|
|
|
|
it('should support buffer() method', () => {
|
|
const url = base;
|
|
const request = new Request(url, {
|
|
method: 'POST',
|
|
body: 'a=1'
|
|
});
|
|
expect(request.url).to.equal(url);
|
|
return request.buffer().then(result => {
|
|
expect(result.toString()).to.equal('a=1');
|
|
});
|
|
});
|
|
|
|
it('should support blob() method', async () => {
|
|
const url = base;
|
|
const request = new Request(url, {
|
|
method: 'POST',
|
|
body: new TextEncoder().encode('a=1')
|
|
});
|
|
expect(request.url).to.equal(url);
|
|
const blob = await request.blob();
|
|
expect(blob).to.be.an.instanceOf(Blob);
|
|
expect(blob.size).to.equal(3);
|
|
expect(blob.type).to.equal('');
|
|
});
|
|
|
|
it('should support clone() method', () => {
|
|
const url = base;
|
|
const body = stream.Readable.from('a=1');
|
|
const agent = new http.Agent();
|
|
const {signal} = new AbortController();
|
|
const request = new Request(url, {
|
|
body,
|
|
method: 'POST',
|
|
redirect: 'manual',
|
|
headers: {
|
|
b: '2'
|
|
},
|
|
follow: 3,
|
|
compress: false,
|
|
agent,
|
|
signal
|
|
});
|
|
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.follow).to.equal(3);
|
|
expect(cl.compress).to.equal(false);
|
|
expect(cl.method).to.equal('POST');
|
|
expect(cl.counter).to.equal(0);
|
|
expect(cl.agent).to.equal(agent);
|
|
expect(cl.signal).to.equal(signal);
|
|
// Clone body shouldn't be the same body
|
|
expect(cl.body).to.not.equal(body);
|
|
return Promise.all([cl.text(), request.text()]).then(results => {
|
|
expect(results[0]).to.equal('a=1');
|
|
expect(results[1]).to.equal('a=1');
|
|
});
|
|
});
|
|
|
|
it('should support ArrayBuffer as body', () => {
|
|
const encoder = new TextEncoder();
|
|
const request = new Request(base, {
|
|
method: 'POST',
|
|
body: encoder.encode('a=1').buffer
|
|
});
|
|
return request.text().then(result => {
|
|
expect(result).to.equal('a=1');
|
|
});
|
|
});
|
|
|
|
it('should support Uint8Array as body', () => {
|
|
const encoder = new TextEncoder();
|
|
const request = new Request(base, {
|
|
method: 'POST',
|
|
body: encoder.encode('a=1')
|
|
});
|
|
return request.text().then(result => {
|
|
expect(result).to.equal('a=1');
|
|
});
|
|
});
|
|
|
|
it('should support DataView as body', () => {
|
|
const encoder = new TextEncoder();
|
|
const request = new Request(base, {
|
|
method: 'POST',
|
|
body: new DataView(encoder.encode('a=1').buffer)
|
|
});
|
|
return request.text().then(result => {
|
|
expect(result).to.equal('a=1');
|
|
});
|
|
});
|
|
|
|
it('should warn once when using .data (request)', () => new Promise(resolve => {
|
|
process.once('warning', evt => {
|
|
expect(evt.message).to.equal('.data is not a valid RequestInit property, use .body instead');
|
|
resolve();
|
|
});
|
|
|
|
// eslint-disable-next-line no-new
|
|
new Request(base, {
|
|
data: ''
|
|
});
|
|
}));
|
|
});
|